JAVA快速入门-JNI_C调用JAVA

上一篇实现了 JAVA 调用 C 语言函数的过程,当然 C 也能通过 JNI 来访问 JAVA 的方法和成员变量,实际上具体调用什么函数以及如何实现这些具体的操作,都在 jni 的手册中有进行讲解。下面以程序来实现 C 调用 JAVA 方法或成员变量的过程。先是一个简单的访问 JAVA 静态成员方法的程序
JAVA代码:

public class Hello {
    /* C来访问这个静态成员方法 */
    public static void main(String args[]){
        System.out.println("Hello World!");
    }
}

C代码:

#include <stdio.h>
#include <jni.h>

/* 创建 java 虚拟机 */
jint create_vm(JavaVM** jvm, JNIEnv** env) 
{  
  JavaVMInitArgs args;  
  JavaVMOption options[1];  
  args.version = JNI_VERSION_1_6;//虚拟机版本号  
  args.nOptions = 1;  
  options[0].optionString = "-Djava.class.path=./";//在当前目录下查找这个类 
  args.options = options;  
  args.ignoreUnrecognized = JNI_FALSE;  
  return JNI_CreateJavaVM(jvm, (void **)env, &args);  
}  

int main(int argc, char **argv)
{
    JavaVM* jvm;
    JNIEnv* env;

    jclass cls;
    int ret = 0;

    jmethodID mid;

    /* 1. 创建 java 虚拟机 */
    if(create_vm(&jvm, &env)){
        printf("can not create jvm\n");
        return -1;
    }

    /* 2. 获取 JAVA 里面的类 */
    cls = (*env)->FindClass(env, "Hello");
    if (cls == NULL) {
        printf("can not find hello class\n");
        ret = -1;
        goto destroy;//返回错误
    }

    /* 3. 获得一个静态方法的ID */
    /* 后面的字段描述符可以使用 javap -p -s Hello.class 来获得 */
    mid = (*env)->GetStaticMethodID(env, cls, "main","([Ljava/lang/String;)V");
    if (mid == NULL) {
        ret = -1;
        printf("can not get method\n");
        goto destroy;
    }

    /* 4. 调用这个静态方法 */
    (*env)->CallStaticVoidMethod(env, cls, mid, NULL);

destroy:    
    (*jvm)->DestroyJavaVM(jvm);
    return ret;
}

如何去编译上面这段代码呢,需要用到如下编译指令:

  • javac Hello.java
  • javap -p -s Hello.class 这个指令用于获得字段描述符该如何写
  • gcc -o caller caller.c -I/usr/lib/jvm/java-1.8.0-openjdk-amd64/include -L /usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/amd64/server -ljvm
  • LD_LIBRARY_PATH=/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/amd64/server ./caller

上面的代码可以实现直接调用 JAVA 里面写好的 main 方法,但是此时的 main 方法是一个 static 类型的方法,如果想调用非 static 方法需要做哪些改动呢,这里在 JAVA 里面再实现一个非 static 的方法然后调用,具体看代码实现。
JAVA 代码:

public class Hello {
    public static void main(String args[]){
        System.out.println("Hello World!");
    }

    /* C 里面调用这个非 static 的方法 */
    public int sayhello_to(String name){
        System.out.println("Hello, "+name);
        return 123;
    }
}

C代码:

#include <stdio.h>
#include <jni.h>

jint create_vm(JavaVM** jvm, JNIEnv** env) 
{  
        JavaVMInitArgs args;  
        JavaVMOption options[1];  
        args.version = JNI_VERSION_1_6;//虚拟机版本号  
        args.nOptions = 1;  
        options[0].optionString = "-Djava.class.path=./";//在当前目录下查找这个类 
        args.options = options;  
        args.ignoreUnrecognized = JNI_FALSE;  
        return JNI_CreateJavaVM(jvm, (void **)env, &args);  
} 

int main(int argc, char **argv)
{
    JavaVM* jvm;
    JNIEnv* env;

    jclass cls;
    int ret = 0;

    jmethodID mid;
    jmethodID cid;

    jobject jobj;
    jstring jstr;

    int r = 0;

    /* 1. 创建java虚拟机 */
    if(create_vm(&jvm, &env)){
        printf("can not create jvm\n");
        return -1;
    }

    /* 2. 获取JAVA里面的 Hello 类 */
    cls = (*env)->FindClass(env, "Hello");
    if (cls == NULL) {
        printf("can not find hello class\n");
        ret = -1;
        goto destroy;
    }

    /* 3. 获得一个构造方法的ID */
    /* 由于sayhello_to不是静态方法,所以这里需要去获得一个构造方法 */
    /* 获取 Hello 类的构造方法ID,这个构造方法不需要参数“()V” */
    cid = (*env)->GetMethodID(env, cls,"<init>", "()V");
    if (cid == NULL) {
        ret = -1;
        printf("can not get constructor method\n");
        goto destroy;
    }

    /* 3.1 构造方法里实例一个对象,因为调用的方法非static */
    jobj = (*env)->NewObject(env, cls, cid);
    if (jobj == NULL) {
        ret = -1;
        printf("can not create object\n");
        goto destroy;
    }


    /* 4. 获得 sayhello_to 方法的ID */
    mid = (*env)->GetMethodID(env, cls, "sayhello_to","(Ljava/lang/String;)I");
    if (mid == NULL) {
        ret = -1;
        printf("can not get method\n");
        goto destroy;
    }

    /* 4.1 构造字符串参数 */
    jstr = (*env)->NewStringUTF(env, "this is string");

    /* 4.2 调用方法 */
    /* 调用 jobj 里面的 mid 方法,并传入一个 jstr参数 */
    r = (*env)->CallIntMethod(env, jobj, mid, jstr);
    printf("ret = %d\n", r);//打印JAVA方法的返回值

destroy:    
    (*jvm)->DestroyJavaVM(jvm);
    return ret;
}

上面程序过程实现了 C 调用 JAVA 里面的非静态成员方法的过程,那么有办法去读取或设置 JAVA 类的中成员变量呢,看下一个代码,实现去调用和设置类里面的成员变量。
JAVA 代码:

public class Hello {
    /* 定义两个成员变量 */
    private String name;
    private int age;

    public static void main(String args[]){
        System.out.println("Hello World!");
    }

    /* 由C调用变量和设置变量后对其进行打印 */
    public int sayhello_to(String name){
        System.out.println("Hello, "+name+"!I am "+this.name+", "+age+" years old.");
        return 123;
    }
}

C代码:

#include <stdio.h>
#include <jni.h>

jint create_vm(JavaVM** jvm, JNIEnv** env) 
{  
        JavaVMInitArgs args;  
        JavaVMOption options[1];  
        args.version = JNI_VERSION_1_6;//虚拟机版本号  
        args.nOptions = 1;  
        options[0].optionString = "-Djava.class.path=./";//在当前目录下查找这个类 
        args.options = options;  
        args.ignoreUnrecognized = JNI_FALSE;  
        return JNI_CreateJavaVM(jvm, (void **)env, &args);  
}  

int main(int argc, char **argv)
{
    JavaVM* jvm;
    JNIEnv* env;

    jclass cls;
    int ret = 0;

    jmethodID mid;
    jmethodID cid;

    jobject jobj;
    jstring jstr;

    jfieldID nameID;
    jfieldID ageID;

    int r = 0;

    /* 1. 创建java虚拟机 */
    if(create_vm(&jvm, &env)){
        printf("can not create jvm\n");
        return -1;
    }

    /* 2. 获取JAVA里面的类 */
    cls = (*env)->FindClass(env, "Hello");
    if (cls == NULL) {
        printf("can not find hello class\n");
        ret = -1;
        goto destroy;
    }

    /* 3. 获得一个构造方法的ID */
    cid = (*env)->GetMethodID(env, cls,"<init>", "()V");
    if (cid == NULL) {
        ret = -1;
        printf("can not get constructor method\n");
        goto destroy;
    }

    /* 3.1 构造方法里实例一个对象,因为调用的方法非static */
    jobj = (*env)->NewObject(env, cls, cid);
    if (jobj == NULL) {
        ret = -1;
        printf("can not create object\n");
        goto destroy;
    }

    /* 3.2 获得 name 属性ID */
    nameID = (*env)->GetFieldID(env, cls, "name", "Ljava/lang/String;");
    if(nameID == NULL){
        ret = -1;
        printf("can not get field name");
        goto destroy;
    }

    /* 3.3 读取/设置 name 字符串为“Bill”*/
    jstr = (*env)->NewStringUTF(env, "Bill");//实例一个字符串
    (*env)->SetObjectField(env, jobj, nameID, jstr);//把name设置为刚创建的Bill


    /* 3.4 获得 age 属性ID */
    ageID = (*env)->GetFieldID(env, cls, "age", "I");
    if(ageID == NULL){
        ret = -1;
        printf("can not get field age");
        goto destroy;
    }

    /* 3.5 读取/设置 age 整形变量为 10*/
    (*env)->SetIntField(env, jobj, ageID, 10);


    /* 4. 获得 sayhello_to 方法的ID */
    mid = (*env)->GetMethodID(env, cls, "sayhello_to","(Ljava/lang/String;)I");
    if (mid == NULL) {
        ret = -1;
        printf("can not get method\n");
        goto destroy;
    }

    /* 4.1 构造字符串参数 */
    jstr = (*env)->NewStringUTF(env, "this is string");

    /* 4.2 调用方法 */
    /* 调用 jobj 里面的 mid 方法,并传入一个 jstr参数 */
    r = (*env)->CallIntMethod(env, jobj, mid, jstr);
    printf("ret = %d\n", r);//打印JAVA方法的返回值

destroy:    
    (*jvm)->DestroyJavaVM(jvm);
    return ret;
}

最后以思维导图做个总结:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值