• JNI学习笔记_Java调用C —— 非Android中使用的方法


    一、学习笔记


    1.java源码中的JNI函数本机方法声明必须使用native修饰。

    2.相对反编译 Java 的 class 字节码文件来说,反汇编.so动态库来分析程序的逻辑要复杂得多,为了应用的安全性,会将一些复杂的逻辑和
    算法通过本地代码(C或C++)来实现,然后打包成.so动态库文件

    3.使用了 JNI 接口的 JAVA 程序,不再像以前那样自由的跨平台。如果要实现跨平台,就必须将本地代码在不同的操作系统平台下编译出相
    应的动态库。

    4.JNI 开发流程主要分为以下 6 步:
    (1)编写声明了 native 方法的 Java 类。
    (2)将 Java 源代码编译成 class 字节码文件。
    (3)用 javah -jni 命令生成.h头文件(-jni 参数表示将 class 中用native 声明的函数生成 JNI 规则的函数)。
    (4)用本地代码实现.h头文件中的函数。
    (5)将本地代码编译成动态库。
    (6)拷贝动态库至 java.library.path 本地库搜索目录下,并运行 Java 程序。

    5.JVM 查找 native 方法的两种方式:
    (1)按照 JNI 规范的命名规则
    (2)调用 JNI 提供的 RegisterNatives 函数,将本地函数注册到 JVM 中。

    6.Native方法签名中的 JNIEXPORT 和 JNICALL 在 Linux/Unix 系统中,这两个宏可以省略不加。

    7.本地实现方法的第二个参数为:如果这个 native 方法是实例方法,则该参数是 jobject,如果是静态方法,则是 jclass。

    8.JNI 把 Java 中的所有对象当作一个C指针传递到本地方法中,这个指针指向 JVM 中的内部数据结构,而内部的数据结构在内存中的存储方
    式是不可见的。只能从 JNIEnv 指针指向的函数表中选择合适的 JNI 函数来操作 JVM 中的数据结构。

    9.JNI 的异常和 Java 中的异常处理流程是不一样的,Java 遇到异常如果没有捕获,程序会立即停止运行。而 JNI 遇到未决的异常不会改变
    程序的运行流程,也就是程序会继续往下走。

    10.字符串操作
    (1)GetStringChars/GetStringUTFChars和ReleaseStringChars/ReleaseStringUTFChars
    用于获取和释放以 Unicode/UTF-8 格式编码的字符串。后者是用于释放。
    (2)GetStringLength/GetStringUTFLength
    由于 UTF-8 编码的字符串以''结尾(也可以使用strlen获取长度),而 Unicode 字符串不是。上面函数获取字符串长度。
    (3)GetStringCritical和ReleaseStringCritical
    提高 JVM 返回源字符串直接指针的可能性,但是获取和释放之间是不能阻塞的也不能调用其它JNI函数。因为获取这个直接指针后会导致暂停
    GC(垃圾回收) 线程,当 GC 被暂停后,如果其它线程触发 GC 继续运行的话,都会导致被阻塞。
    (4)GetStringRegion和GetStringUTFRegion
    分别表示获取 Unicode 和 UTF-8 编码字符串指定范围内的内容到一个预先分配好的缓冲区中,这个函数不会分配内存,因此也没有释放的函数。

    11.JNI 中的数组分为基本类型数组和对象数组,它们的处理方式是不一样的,基本类型数组中的所有元素都是 JNI 的基本数据类型,可以直
    接访问。而对象数组中的所有元素是一个类的实例或其它数组的引用,和字符串操作一样,不能直接访问 Java 传递给 JNI 层的数组,必须选
    择合适的 JNI 函数来访问和设置 Java 层的数组对象。

    12.GC 会实时扫描所有创建的对象是否还有引用,如果没有引用则会立即清理掉。当我们创建一个像 int 数组对象的时候,当我们在本地代码
    想去访问时,发现这个对象正被 GC 线程占用了,这时本地代码会一直处于阻塞状态,直到等待 GC 释放这个对象的锁之后才能继续访问。为了
    避免这种现象的发生,JNI 提供了 Get/ReleasePrimitiveArrayCritical 这对函数,本地代码在访问数组对象时会暂停 GC 线程。同样这两个函
    数之间不能阻塞或调用其它JNI函数

    13.在 JNI 中,只有 jobject 以及子类属于引用变量,会占用引用表的空间,jint,jfloat,jboolean 等都是基本类型变量,不会占用引用
    表空间,即不需要释放。引用表最大空间为 512 个,如果超出这个范围,JVM 就会挂掉。

    14.本地代码调用 Java 层某个对象的方法或属性,这就是来自 C/C++层本地函数的 callback(回调)。

    15.引用类型统一调用CallStaticObjectMethod 函数.(不存在CallStaticStringMethod)。

    16.GetMethodID/GetStaticMethodID 和 CallIntMethod/CallStaticIntMethod 和 SetIntField/SetStaticIntField,对应static和非static成员方法的调用函数不同。

    17.JNI方法签名的格式为:(形参参数类型列表)返回值。形参参数列表中,引用类型以 L 开头,后面紧跟类的全路径名(需将.全部替换成/),
    以分号结尾。

    18.GetMethodID()返回的jmethodID应该不是引用,不需要对它调用DeleteLocalRef()。

    19.调用 GetMethodID 获取方法 ID 和调用 FindClass 获取 Class 实例后,要做异常判断

    20.在 Java 中任何一个类的.class字节码文件被加载到内存中之后,该class子节码文件统一使用 Class 类来表示该类的一个引用(相当于
    Java 中所有类的基类是 Object一样)。然后就可以从该类的 Class 引用中动态的获取类中的任意方法和属性。

    21.在本地代码中可以调用 JNI 函数可以访问 Java 对象中的非 public(eg private) 属性和方法。

    22.由于 ID 对于特定类是相同的,因此只需要查找一次,然后便可重复使用。同样,查找类对象的开销也很大,因此也应该缓存它们。

    23.class 和 member id 在一定范围内是稳定的,但在动态加载的 class loader 下,保存全局的 class 要么可能失效,要么可能造成无法卸载classloader。

    24.(*env)->FindClass()返回的jclass类型的变量是local referenced的,不能被缓存,因为一次JNI调用返回后它引用的已经回收了,下次在
    调用JNI函数时出问题。

    25.方法ID和域ID应该是可以缓存的(它不是引用,而且是稳定存在的)。

    26.如果在用使用时缓存的 ID,要注意只要本地代码依赖于这个 ID 的值,那么这个类就不会被 unload。另外一方面,如果缓存发生在静态初
    始化时,当类被 unload 或 reload 时,ID 会被重新计算。因此,尽量在类静态初始化时就缓存字段 ID、方法 ID。

    27.有两种域ID的缓存方法:
    静态缓存:在静态代码块中调用JNI native函数缓存。
    使用时缓存:就是在正常调用native函数时进行缓存。

    28.三种引用介绍
    (1)局部引用:
    通过 NewLocalRef() 和各种 JNI 接口创建(FindClass、NewObject、GetObjectClass和NewCharArray等)。LocalRef 会阻止 GC 回收所引用
    的对象。JNI函数返回后局部引用所引用的对象会被 JVM 自动释放,或在JNI函数中调用 DeleteLocalRef 释放。(*env)->DeleteLocalRef(env,local_ref)

    局部引用只有在创建它的本地方法返回前有效,本地方法返回到 Java 层之后,如果 Java 层没有对return的局部引用使用的话,局部引用就会被
    JVM 自动释放。在JNI函数中将局部引用存储在静态变量中缓存起来,供下次调用时使用是错误的。

    JNI 会将创建的局部引用都存储在一个局部引用表中,如果这个表超过了最大容量限制,就会造成局部引用表溢出,使程序崩溃。经测试,Android 上的
    JNI 局部引用表最大数量是 512 个。不用时注意及时调用DeleteLocalRef().

    JNI 提供了一系列函数来管理局部引用的生命周期。这些函数包括:EnsureLocalCapacity、NewLocalRef、PushLocalFrame、PopLocalFrame、DeleteLocalRef。

    局部引用不能跨线程使用,只在创建它的线程有效。不要试图在一个线程中创建局部引用并存储到全局引用中,然后在另外一个线程中使用。

    (2)全局引用:
    调用 NewGlobalRef() 基于局部引用创建,会阻止 GC 回收所引用的对象。可以跨方法、跨线程使用。JVM 不会自动释放,必须调用 DeleteGlobalRef() 手
    动释放。(*env)->DeleteGlobalRef(env, g_cls_string)

    只能通过 NewGlobalRef 函数创建全局引用。

    (3)弱全局引用:
    调用 NewWeakGlobalRef() 基于局部引用或全局引用创建,不会阻止 GC 回收所引用的对象,可以跨方法、跨线程使用。引用不会自动释放,在 JVM 认为应
    该回收它的时候(比如内存紧张的时候)进行回收而被释放。或调用DeleteWeakGlobalRef() 手动释放。(*env)->DeleteWeakGlobalRef(env,g_cls_string)

    29.在管理局部引用的生命周期中,Push/PopLocalFrame 是非常方便且安全的。我们可以在本地函数的入口处调用PushLocalFrame,然后在出口处调用
    PopLocalFrame,这样的话,在函数内任何位置创建的局部引用都会被释放。而且,这两个函数是非常高效的,强烈建议使用它们。需要注意的是,
    如果在函数的入口处调用了PushLocalFrame,记住要在函数所有出口(有 return 语句出现的地方)都要调用 PopLocalFrame。

    30.JNI函数签名
    JNIEXPORT void JNICALL Java_HelloWorld_print(JNIEnv *, jobject); //这两个宏可以忽略
    JNIEnv:JNIEnv接口指针指向包含指向函数表的指针的位置。函数表中的每个条目都指向一个JNI函数。本机方法始终通过其中一个JNI函数访问Java虚拟机中的数据结构。
    jobject:是对HelloWorld对象本身的引用(有点像C ++中的“this”指针),若是java中的静态native方法,参数二是jclass.

    JNIEXPORT和JNICALL宏(在jni.h头文件中定义)确保从本机库导出此函数和C编译器生成具有此函数的正确调用约定的代码。
    在/usr/lib/jvm/java-1.7.0-openjdk-amd64/include/jni_md.h中(jni.h中包含它):
    #define JNIEXPORT __attribute__((visibility("default")))
    #define JNICALL //空宏

    31.编译本地方法
    $ gcc -fPIC -shared HelloWorld.c -o libHelloWorld.so -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/
    5.运行
    $ export LD_LIBRARY_PATH=./ 或 setenv LD_LIBRARY_PATH . 或 不设置环境变量运行$ java -Djava.library.path=. HelloWorld
    $ java HelloWorld

    32.JNI以不同方式处理原始类型(java中的8种基本类型)和引用类型; 实例方法和静态方法。

    33.C和C++使用JNI的区别
    从jni.h中关于__cplusplus的条件编译来看,native中的类型和JNIenv中提供的函数都是不同的!
    #ifdef __cplusplus
    typedef JNIEnv_ JNIEnv; //C++使用这个JNIEnv函数集合,它里面的functions域内嵌了JNINativeInterface_!
    #else
    typedef const struct JNINativeInterface_ *JNIEnv; //C使用这个JNIEnv函数集合
    #endif

    二、试验Demo

    1.互传基本类型测试

    /* hello_world.c */
    #include <jni.h>
    #include <stdio.h>
    #include "com_study_jnilearn_HelloWorld.h"
    
    JNIEXPORT jint JNICALL Java_com_study_jnilearn_HelloWorld_test
        (JNIEnv *env, jclass cls, jshort s, jint i, jlong l, jfloat f, jdouble d, jchar c, jboolean z, jbyte b)
    {
        jint ret = 250;
    
        printf("C: s=%hd, i=%d, l=%ld, f=%f, d=%lf, c=%c, z=%d, b=%d
    ", s, i, l, f, d, c, z, b);  
    
        return ret;
    }
    /* HelloWorld.java */
    package com.study.jnilearn;  
    
    public class HelloWorld {  
    
        public static native int test(short s, int i, long l, float f, double d, char c, boolean z, byte b);  
    
        public static void main(String[] args) {  
            short s = 1;  
            long l = 20;  
            byte b = 127;  
            int ret;
            ret = test(s, 1, l, 1.0f, 10.5, 'A', true, b);
            System.out.println("java: ret=" + ret);
        }  
    
        static {  
            System.loadLibrary("hello_world");  
        }  
    }
    编译:
    $ javac HelloWorld.java -d ./
    $ javah -jni com.study.jnilearn.HelloWorld
    $ gcc -shared -fPIC hello_world.c -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -o libhello_world.so
    运行:
    $ java com.study.jnilearn.HelloWorld 
    C: s=1, i=1, l=20, f=1.000000, d=10.500000, c=A, z=1, b=127
    java: ret=250

    2.互传String类型测试

    /* StringTest.java */
    package com.study.jnilearn;  
    
    public class StringTest {  
    
        public native static String sayHello(String text);  
    
        public static void main(String[] args) {  
            System.out.println("Java send: Hello, I am Java"); 
            String text = sayHello("Hello, I am Java");  
            System.out.println("Java get: " + text);  
        }  
    
        static {  
            System.loadLibrary("string_test");  
        }  
    }
    /* string_test.c */
    #include <stdio.h>
    #include <jni.h>
    
    JNIEXPORT jstring JNICALL Java_com_study_jnilearn_StringTest_sayHello
        (JNIEnv *env, jclass cls, jstring j_str)
    {  
        const char *str_get = NULL;  
        const char *str_send = "Hello I am C";  
        jboolean isCopy; //返回JNI_TRUE表示原字符串的拷贝,返回JNI_FALSE表示返回原字符串的指针
        
        str_get = (*env)->GetStringUTFChars(env, j_str, &isCopy);  
        if(str_get == NULL) {  
            return NULL;  
        }  
        printf("C get: %s, isCopy=%d
    ", str_get, isCopy);  
        (*env)->ReleaseStringUTFChars(env, j_str, str_get);
    
        printf("C send: %s
    ", str_send);  
        return (*env)->NewStringUTF(env, str_send);  
    }
    编译:
    $ javac StringTest.java -d ./
    $ javah -jni com.study.jnilearn.StringTest
    $ gcc -shared -fPIC string_test.c -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -o libstring_test.so
    运行:
    $ java com.study.jnilearn.StringTest
    Java send: Hello, I am Java
    C get: Hello, I am Java, isCopy=1
    C send: Hello I am C
    Java get: Hello I am C

    3.基本类型数组类型双向传参

    /*IntArray.java*/
    class IntArray {
    
        private native int[] sortArray(int[] arr);
    
        public static void main(String[] args) {
            IntArray obj = new IntArray();
            int arr1[] = new int[10];
    
            for (int i = 0; i < 10; i++) {
                arr1[i] = i;
            }
    
            int arr2[] = obj.sortArray(arr1);
    
            for (int i = 0; i < 10; i++) {
                System.out.print(arr2[i] + " ");
            }
            System.out.println("");
        }
        
        static {
            System.loadLibrary("IntArray");
        }
    }
    /* IntArray.c */
    #include <jni.h>
    #include <stdio.h>
    #include <string.h>
    #include "IntArray.h"
    
    void arr_short(jint *arr, jint num) {
        jint i, j, tmp;
    
        for (i = 0; i < num; i++) {
            for (j = i; j < num; j++) {
                if (arr[i] < arr[j]) {
                    tmp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = tmp;
                }
            }
        }
    }
    
    JNIEXPORT jintArray JNICALL Java_IntArray_sortArray
        (JNIEnv *env, jobject obj, jintArray jarr)
    {
        jint *array, length;
        jint buff[64] = {0};
        array = (*env)->GetIntArrayElements(env, jarr, NULL);
        if (array == NULL) {
            return 0; /* exception occurred */
        }
        length = (*env)->GetArrayLength(env, jarr);
        memcpy(buff, array, length * sizeof(jint));
        (*env)->ReleaseIntArrayElements(env, jarr, array, 0);
    
        /*不能直接传jarr然后返回jarr,因为其是一个local reference, native调用后就被回收了*/
        arr_short(buff, length); 
    
        jintArray new_arr = (*env)->NewIntArray(env, length); 
        (*env)->SetIntArrayRegion(env, new_arr, 0, length, buff);
        
        return new_arr;
    }
    编译:
    $ javac IntArray.java
    $ javah -jni IntArray
    $ gcc -shared -fPIC IntArray.c -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -o libIntArray.so
    运行:
    $ java IntArray 
    9 8 7 6 5 4 3 2 1 0 

    4.JNI函数中回调类的静态方法和实例方法

    /* AccessMethod.java */
    package com.study.jnilearn;  
    
    class ClassMethod {  
    
        private static int javaStaticCallback(String str, int i) {
            int ret = 1;
            System.out.format("Java: javaStaticCallback: str=%s " + " i=%d
    ", str, i);
            return ret;
        }  
    
        private int javaInstanceCallback(String str, int i) {
            int ret = 2;
            System.out.format("Java: javaInstanceCallback: str=%s " + "i=%d
    ", str, i);
            return ret;
        }  
    }  
    
    
    public class AccessMethod {  
    
        public static native int javaStaticMethod();   
        public static native int javaInstaceMethod();  
    
        public static void main(String[] args) {
            int ret1, ret2;
            ret1 = javaStaticMethod();  
            ret2 = javaInstaceMethod();
            System.out.println("ret1=" + ret1 + " ret2=" + ret2);
        }  
    
        static {  
            System.loadLibrary("AccessMethod");  
        }  
    }
    /* AccessMethod.c */
    #include <stdio.h>
    #include "com_study_jnilearn_AccessMethod.h"  
    
    JNIEXPORT jint JNICALL Java_com_study_jnilearn_AccessMethod_javaStaticMethod(JNIEnv *env, jclass cls)  
    {  
        jclass clazz = NULL;  
        jstring str_arg = NULL;  
        jmethodID method_id;
        jint ret = 0;
    
        // 1、从classpath路径下搜索ClassMethod这个类,并返回该类的Class对象  
        clazz =(*env)->FindClass(env, "com/study/jnilearn/ClassMethod");  
        if (clazz == NULL) {
            printf("Couldn't find class: com/study/jnilearn/ClassMethod");  
            return;  
        }  
    
        // 2、从clazz类中查找javaStaticCallback方法  
        method_id = (*env)->GetStaticMethodID(env, clazz, "javaStaticCallback", "(Ljava/lang/String;I)I");  
        if (method_id == NULL) {  
            printf("Couldn't find method: javaStaticCallback");  
            return;  
        }  
    
        // 3、调用clazz类的callStaticMethod静态方法  
        str_arg = (*env)->NewStringUTF(env, "C: str pass to java static method");  
        ret = (*env)->CallStaticIntMethod(env, clazz, method_id, str_arg, 111);  
    
        // 4.删除局部引用  
        (*env)->DeleteLocalRef(env, clazz);  
        (*env)->DeleteLocalRef(env, str_arg);   // id is not reference,needn't to delete
    
        return ret;
    }  
    
    
    JNIEXPORT jint JNICALL Java_com_study_jnilearn_AccessMethod_javaInstaceMethod(JNIEnv *env, jclass cls)  
    {  
        jclass clazz = NULL;  
        jobject jobj = NULL;  
        jmethodID mid_construct = NULL;  
        jmethodID mid_instance = NULL;  
        jstring str_arg = NULL;
        jint ret = 0;
    
        // 1、从classpath路径下搜索ClassMethod这个类,并返回该类的Class对象  
        clazz = (*env)->FindClass(env, "com/study/jnilearn/ClassMethod");  
        if (clazz == NULL) {  
            printf("Couldn't find class: com/study/jnilearn/ClassMethod");  
            return;  
        }  
    
        // 2、获取类的默认构造方法ID  
        mid_construct = (*env)->GetMethodID(env,clazz, "<init>", "()V");  
        if (mid_construct == NULL) {  
            printf("Couldn't find construct method");  
            return;  
        }  
    
        // 3、查找实例方法的ID  
        mid_instance = (*env)->GetMethodID(env, clazz, "javaInstanceCallback", "(Ljava/lang/String;I)I");  
        if (mid_instance == NULL) {  
            return;  
        }  
    
        // 4、创建该类的实例  
        jobj = (*env)->NewObject(env, clazz, mid_construct);  
        if (jobj == NULL) {  
            printf("Couldn't create new object");  
            return;  
        }  
    
        // 5、调用对象的实例方法  
        str_arg = (*env)->NewStringUTF(env, "C: str pass to java instance method");  
        ret = (*env)->CallIntMethod(env, jobj, mid_instance, str_arg, 222);  
    
        // 删除局部引用  
        (*env)->DeleteLocalRef(env, clazz);  
        (*env)->DeleteLocalRef(env, jobj);  
        (*env)->DeleteLocalRef(env, str_arg);
    
        return ret;
    }
    编译:
    $ javac AccessMethod.java -d ./
    $ javah -jni com.study.jnilearn.AccessMethod
    $ gcc -shared -fPIC AccessMethod.c -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -o libAccessMethod.so
    运行:
    $ java com.study.jnilearn.AccessMethod 
    Java: javaStaticCallback: str=C: str pass to java static method  i=111
    Java: javaInstanceCallback: str=C: str pass to java instance method i=222
    ret1=1 ret2=2

    5.JNI函数中使用静态和非静态成员属性

    /* AccessField.java */
    package com.study.jnilearn;  
    
    class ClassField {  
    
        private static int num;  
        private String str;  
    
        public int getNum() {  
            return num;  
        }  
    
        public void setNum(int num) {  
            this.num = num;
        }  
    
        public String getStr() {  
            return str;  
        }  
    
        public void setStr(String str) {  
            this.str = str;  
        }  
    }
    
    public class AccessField {  
    
        private native static void accessInstanceField(ClassField obj);  
    
        private native static void accessStaticField();  
    
        public static void main(String[] args) {  
            ClassField obj = new ClassField();  
            obj.setNum(10);  
            obj.setStr("Hello");  
    
            // 本地代码访问和修改ClassField为中的静态属性num  
            accessStaticField();  
            accessInstanceField(obj);  
    
            System.out.println("Java: ClassField.num = " + obj.getNum());  
            System.out.println("Java: ClassField.str = " + obj.getStr());  
        }  
    
        static {  
            System.loadLibrary("AccessField");  
        }  
    }
    #include "com_study_jnilearn_AccessField.h"  
    
    JNIEXPORT void JNICALL Java_com_study_jnilearn_AccessField_accessInstanceField  
    (JNIEnv *env, jclass cls, jobject obj)  
    {  
        jclass clazz;  
        jfieldID fid;  
        jstring j_str;  
        jstring j_newStr;  
        const char *c_str = NULL;  
    
        // 1.获取AccessField类的Class引用  
        clazz = (*env)->GetObjectClass(env, obj);  
        if (clazz == NULL) {  
            return;  
        }  
    
        // 2. 获取AccessField类实例变量str的属性ID  
        fid = (*env)->GetFieldID(env, clazz, "str", "Ljava/lang/String;");  
        if (clazz == NULL) {  
            return;  
        }  
    
        // 3. 获取实例变量str的值  
        j_str = (jstring)(*env)->GetObjectField(env, obj, fid);  
    
        // 4. 将unicode编码的java字符串转换成C风格字符串  
        c_str = (*env)->GetStringUTFChars(env, j_str, NULL);  
        if (c_str == NULL) {  
            return;  
        }  
        printf("C: ClassField.str = %s
    ", c_str);  
        (*env)->ReleaseStringUTFChars(env, j_str, c_str);  
    
        // 5. 修改实例变量str的值  
        j_newStr = (*env)->NewStringUTF(env, "World");  
        if (j_newStr == NULL) {  
            return;  
        }  
    
        (*env)->SetObjectField(env, obj, fid, j_newStr);  
    
        // 6.删除局部引用  
        (*env)->DeleteLocalRef(env, clazz);  
        (*env)->DeleteLocalRef(env, j_str);  
        (*env)->DeleteLocalRef(env, j_newStr);  
    }  
    
    JNIEXPORT void JNICALL Java_com_study_jnilearn_AccessField_accessStaticField  
    (JNIEnv *env, jclass cls)  
    {  
        jclass clazz;  
        jfieldID fid;  
        jint num;  
    
        // 1.获取ClassField类的Class引用  
        clazz = (*env)->FindClass(env, "com/study/jnilearn/ClassField");  
        if (clazz == NULL) {
            return;  
        }  
    
        // 2.获取ClassField类静态变量num的属性ID  
        fid = (*env)->GetStaticFieldID(env, clazz, "num", "I");  
        if (fid == NULL) {  
            return;  
        }  
    
        // 3.获取静态变量num的值  
        num = (*env)->GetStaticIntField(env, clazz, fid);  
        printf("C: ClassField.num = %d
    ", num);  
    
        // 4.修改静态变量num的值  
        (*env)->SetStaticIntField(env, clazz, fid, 88);  
    
        // 删除属部引用  
        (*env)->DeleteLocalRef(env, clazz);  
    }
    编译:
    $ javac AccessField.java -d ./
    $ javah -jni com.study.jnilearn.AccessField
    $ gcc -shared -fPIC AccessField.c -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -o libAccessField.so
    运行:
    $ java com.study.jnilearn.AccessField
    C: ClassField.num = 10
    C: ClassField.str = Hello
    Java: ClassField.num = 88
    Java: ClassField.str = World

    6.优化:类静态初始化缓存

    /* AccessCache.java */
    public class AccessCache {
    
        public static native void initIDs(); 
    
        public native void nativeMethod();
        public void callback() {
            System.out.println("Java: AccessCache.callback invoked!");
        }
    
        public static void main(String[] args) {
            AccessCache accessCache = new AccessCache();
            accessCache.nativeMethod();
        }
    
        static {
            System.loadLibrary("AccessCache");
            initIDs();
        }
    }
    #include <stdio.h>
    #include "AccessCache.h"
    
    jmethodID MID_AccessCache_callback;
    
    JNIEXPORT void JNICALL Java_AccessCache_initIDs(JNIEnv *env, jclass cls)
    {
        printf("initIDs called!
    ");
        MID_AccessCache_callback = (*env)->GetMethodID(env, cls, "callback", "()V");
    }
    
    JNIEXPORT void JNICALL Java_AccessCache_nativeMethod(JNIEnv *env, jobject obj)
    {
        printf("C: call java's callback()
    ");
        (*env)->CallVoidMethod(env, obj, MID_AccessCache_callback);
    }
    编译:
    javac AccessCache.java
    javah -jni AccessCache
    $ gcc -shared -fPIC AccessCache.c -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -o libAccessCache.so
    运行:
    $ java AccessCache 
    initIDs called!
    C: call java's callback()
    Java: AccessCache.callback invoked!

    jni.pdf翻译总结版:http://wiki.jikexueyuan.com/project/jni-ndk-developer-guide/recommend.html

  • 相关阅读:
    elasticsearch 基础 —— 集群原理
    剑指 offer set 10 栈的压入、弹出序列
    剑指 offer set 9 包含min函数的栈
    剑指 offer set 8 树的子结构
    剑指 offer set 7 调整数组顺序使奇数位于偶数前面
    剑指 offer set 6 打印从 1 到 N 的所有数
    剑指 offer set 5 二进制中 1 的个数
    剑指 offer set 4 矩形覆盖
    剑指 offer set 3 旋转数组的最小数字
    剑指 offer set 2 从头到尾打印链表
  • 原文地址:https://www.cnblogs.com/hellokitty2/p/10768883.html
Copyright © 2020-2023  润新知