美文网首页
Android NDK开发(第三天)

Android NDK开发(第三天)

作者: arkliu | 来源:发表于2018-07-22 15:00 被阅读0次

    JNIEnv类型

    JNIEnv类型实际上代表了java环境,通过JNIEnv* 指针就可以对java端的代码进行操作,例如,创建java类中的对象,调用java对象的方法,获取java对象中的属性等,常用的类型如下:

    #创建java类中的对象
    jobject NewObject(jclass clazz, jmethodID methodID, ...)
    
    #创建java类中的string对象
    jstring NewString(const jchar* unicodeChars, jsize len)
    
    #创建特定类型的数组
    jobjectArray NewObjectArray(jsize length, jclass elementClass,
            jobject initialElement)
    
    #获取类型为type的字段
    jfieldID GetFieldID(jclass clazz, const char* name, const char* sig)
    

    更多的函数,可以查看jni.h中的函数名称

    jclass类型

    为了能够在C/C++中使用java类,jni.h头文件中专门定义了jclass类型来表示java中的class类

    JNIEnv类中有如下几个简单的函数可以取得jclass:

    • jclass FindClass(const char* name)
      通过类全名获取jclass,如:jclass str = env->FindClass("java/lang/String")

    • jclass GetObjectClass(jobject obj)
      

    通过对象实例获取jclass,类似于java中的getClass方法

    • jclass GetSuperclass(jclass clazz)
      通过class获取其父类的class对象

    native中访问java层方法

    在native层访问java端的代码,最常见的就是获取类的属性,和调用类的方法。为了在c/c++中表示属性和方法,JNI在jni.h中定义了jfieldId,jmethodId类型来分别表示java端的属性和方法。

    使用下面方法获取jfield和jmethod

    • GetFieldID / GetMethodID
    • GetStaticFieldID / GetStaticMethodID
    jfieldID GetFieldID(jclass clazz, const char* name, const char* sig)
    
    参数说明:
    # clazz: 这个方法依赖的类对象的class对象
    # name 这个字段的名称
    # sig这个字段的签名
    
    可以通过javap查看勒种的字段和方法的签名如:
     
    

    查看类中字段和方法的签名:

    public class DataProvider {
    
        /**
         * 把java中的int传递给c语言,c语言处理完毕后,把结果相加返回给java
         * @param x
         * @param y
         * @return
         */
        public static native int add(int x, int y);
    
        /**
         * 把java中的string传递给c语言,c语言获取到java的string之后,在string后面添加一个hello字符串
         * @param s
         * @return
         */
        public static native String sayHelloInC(String s);
    
        static {
            System.loadLibrary("test-lib");
        }
        
    }
    

    使用javap -s -p ./app/build/intermediates/classes/debug/com/jni/test/DataProvider.class, 命令执行结果如下:


    image.png

    下面看个栗子:

    新建Hello.java

    import android.util.Log;
    
    import java.util.Date;
    
    /**
     * Created by liuhang on 18-7-20.
     */
    
    public class Hello {
    
        public int property;
        public int function(int foo, Date date, int[]arr) {
            Log.e("liuhang", "function runs .....");
            return 0;
        }
    
        public native void test();
    
        static {
            System.loadLibrary("test-lib");
        }
    
    }
    

    Hello.java中属性和方法签名如下:

    Compiled from "Hello.java"
    public class com.jni.test.Hello {
      public int property;
        Signature: I
      public com.jni.test.Hello();
        Signature: ()V
    
      public int function(int, java.util.Date, int[]);
        Signature: (ILjava/util/Date;[I)I
    
      public native void test();
        Signature: ()V
    }
    
    

    首先使用javah生成hello.java对应的头文件

    javah com.jni.test.Hello
    

    编写hello.cpp文件,在native test方法中,调用java中的function方法

    #include <jni.h>
    #include <string>
    #include <android/log.h>
    #define  LOG_TAG    "liuhang"
    #define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
    
    extern "C"
    
    /*
     * Class:     com_jni_test_Hello
     * Method:    test
     * Signature: ()V
     */
    JNIEXPORT void JNICALL Java_com_jni_test_Hello_test
      (JNIEnv * env, jobject obj) {
      jclass hello_clazz = env->GetObjectClass(obj);
      jfieldID fieldId_prop = env->GetFieldID(hello_clazz, "property", "I");
    
      jmethodID methodId = env->GetMethodID(hello_clazz, "function", "(ILjava/util/Date;[I)I");
    
      env->CallIntMethod(obj, methodId, 0L, NULL, NULL);
    }
    
    
    
    
    

    此时执行test方法,就会在native中调用java中的function方法

    new Hello().test();
    

    结果如下:


    image.png

    native中更改java类属性值

    创建ChangeProp.java

    package com.jni.test;
    
    /**
     * Created by liuhang on 18-7-20.
     */
    
    public class ChangeProp {
    
        public String name = "zhangsan";
        public static int number = 9;
    
        public native void changePropFun();
    
        static {
            System.loadLibrary("test-lib");
        }
    }
    
    

    对应的签名如下:

    public class com.jni.test.ChangeProp {
      public java.lang.String name;
        Signature: Ljava/lang/String;
      public static int number;
        Signature: I
      public com.jni.test.ChangeProp();
        Signature: ()V
    
      public native void changePropFun();
        Signature: ()V
    
      static {};
        Signature: ()V
    }
    
    

    使用javah生成头文件

    image.png

    创建changeprop.cpp

    #include <jni.h>
    #include <string>
    #include <android/log.h>
    #include <malloc.h>
    #define  LOG_TAG    "liuhang"
    #define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
    
    extern "C"
    
    
    char* Jstring2CStr(JNIEnv*   env,   jstring   jstr)
    {
         char*   rtn   =   NULL;
         jclass   clsstring   =   (env)->FindClass("java/lang/String");
         jstring   strencode   =   (env)->NewStringUTF("GB2312");
         jmethodID   mid   =   (env)->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
         jbyteArray   barr=   (jbyteArray)(env)->CallObjectMethod(jstr,mid,strencode); // String .getByte("GB2312");
         jsize   alen   =   (env)->GetArrayLength(barr);
         jbyte*   ba   =   (env)->GetByteArrayElements(barr,JNI_FALSE);
         if(alen   >   0)
         {
          rtn   =   (char*)malloc(alen+1);         //"\0"
          memcpy(rtn,ba,alen);
          rtn[alen]=0;
         }
         (env)->ReleaseByteArrayElements(barr,ba,0);  //
         return rtn;
    }
    
    
    extern "C"
    /*
     * Class:     com_jni_test_ChangeProp
     * Method:    changePropFun
     * Signature: ()V
     */
    JNIEXPORT void JNICALL Java_com_jni_test_ChangeProp_changePropFun
      (JNIEnv *env, jobject jobject) {
        jclass jclazz = env->GetObjectClass(jobject);
    
        // 分别获取两个属性的id,,Ljava/lang/String; 这里的签名一定要由分号
        jfieldID jfield_name = env->GetFieldID(jclazz, "name", "Ljava/lang/String;");
        jfieldID jfiled_number = env->GetStaticFieldID(jclazz, "number", "I");
        // 获取属性的值,需要注意string是object类型
        jstring before_name = (jstring)env->GetObjectField(jobject, jfield_name);
        jint before_number = env->GetStaticIntField(jclazz, jfiled_number);
        // 打印修改前的属性值
        LOGI("modify before name is %s ,, number is :%d", Jstring2CStr(env, before_name), before_number);
    
        // 设置新的属性值
        env->SetObjectField(jobject, jfield_name, env->NewStringUTF("lisi"));
        env->SetStaticIntField(jclazz, jfiled_number, 44);
    
    }
    
    
    
    

    java代码调用

    ChangeProp prop = new ChangeProp();
    Log.e("liuhang", "in activity modify before name is :"+prop.name+" number is :"+ChangeProp.number);
            prop.changePropFun();
    Log.e("liuhang", "in activity modify after name is :"+prop.name+" number is :"+ChangeProp.number);
    

    此时打印如下:


    image.png
    JNIEnv中,提供了众多的 Call<Type>Method和 CallStatic<Type>method,
    还有CallNonvirtual<Type>Method函数,需要通过GetMethodID获取相
    对应的方法的jmethodID来传入到上述函数参数中
    

    调用示例方法的三种形式如下:

    Call<Type>Method(jobject obj, jmethodID id, ....);
    Call<Type>Method(jobject obj, jmethodID id, va_list lst);
    Call<Type>Method(jobject obj, jmethodID id, jvalue * v);
    

    java和c++中的多态机制

    C++和java对于继承后执行的是父类还是子类的方法是由区别的,在java中,所有的方法都是虚拟的,所以总是调用子类的方法,因此CallNonVirtual<Type>Method方法就出来了,这个方法可以帮助调用java中父亲的方法。看下面demo

    创建ExtendsDemo.java

    package com.jni.test;
    
    /**
     * Created by liuhang on 18-7-21.
     */
    
    public class ExtendsDemo {
    
        public Father father = new Child();
    
        public native void testExtends();
    
        static {
            System.loadLibrary("test-lib");
        }
    
    }
    
    

    对应的方法签名如下:

    public class com.jni.test.ExtendsDemo {
      public com.jni.test.Father father;
        Signature: Lcom/jni/test/Father;
      public com.jni.test.ExtendsDemo();
        Signature: ()V
    
      public native void testExtends();
        Signature: ()V
    
      static {};
        Signature: ()V
    }
    
    

    创建父类Father.java和子类Child.java

    package com.jni.test;
    
    import android.util.Log;
    
    /**
     * Created by liuhang on 18-7-21.
     */
    
    public class Father {
    
        public void function() {
            Log.e("liuhang", "father function runs.....");
        }
    }
    
    
    
    package com.jni.test;
    
    import android.util.Log;
    
    /**
     * Created by liuhang on 18-7-21.
     */
    
    public class Child extends Father {
    
        @Override
        public void function() {
            Log.e("liuhang", "child function runs....");
        }
    }
    
    
    

    为ExtendsDemo生成头文件

    liuhang@android:~/code/MyApplication/app/src/main/java$ javah com.jni.test.ExtendsDemo
    
    

    创建Extends.cpp

    #include <jni.h>
    #include <string>
    #include <android/log.h>
    #define  LOG_TAG    "liuhang"
    #define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
    
    extern "C"
    /*
     * Class:     com_jni_test_ExtendsDemo
     * Method:    testExtends
     * Signature: ()V
     */
    JNIEXPORT void JNICALL Java_com_jni_test_ExtendsDemo_testExtends
      (JNIEnv * env, jobject obj) {
    
        LOGI("liuhang jni testExtends runs...");
        // 获取ExtendsEemo类对应的class对象
        jclass clazz = env->GetObjectClass(obj);
        // 获取father属性对应的filedid
        jfieldID id_father = env->GetFieldID(clazz, "father", "Lcom/jni/test/Father;");
        // 获取father属性对象
        jobject father = env->GetObjectField(obj, id_father);
    
    
        // 获取father类
        jclass jfather_clazz = env->FindClass("com/jni/test/Father");
        // 获取Father类中的function方法
        jmethodID jfatherFun_id = env->GetMethodID(jfather_clazz, "function", "()V");
        // 调用子类的方法
        env->CallVoidMethod(father, jfatherFun_id);
        // 调用父类的方法
        env->CallNonvirtualVoidMethod(father, jfather_clazz, jfatherFun_id);
    
    }
    
    
    

    java中调用native方法

    new ExtendsDemo().testExtends();
    

    此时执行结果如下:
    [图片上传失败...(image-23a951-1532242821008)]

    native中创建java对象

    在JNIEnv中有两种方法创建java对象,下面逐个介绍

    通过NewObject创建java对象

    jobject NewObject(jclass clazz, jmethodID methodID, ...)
    
    #clazz是需要构建的java对象的class类对象
    #methodID是传递一个方法ID,就是构造方法
    
    #因为构造方法没有返回值,所以认为类的默认构造方法的返回值类型的签名始终是"()V",(因为默认的构造方法是没有参数的),方法的名称始终为"<init>"
    

    在native中创建Date对象,打印getTime返回值

    创建ConstructJavaObj.java
    package com.jni.test;
    
    
    /**
     * Created by liuhang on 18-7-21.
     */
    
    public class ConstructJavaObj {
    
        public native void firstConstruct();
        public native void secondConstruct();
    
        static {
            System.loadLibrary("test-lib");
        }
    
    }
    
    
    使用javah生成头文件
    liuhang@android:~/code/MyApplication/app/src/main/java$ javah com.jni.test.ConstructJavaObj
    
    创建constructobj.cpp
    #include <jni.h>
    #include <string>
    #include <android/log.h>
    #define  LOG_TAG    "liuhang"
    #define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
    
    extern "C"
    
    /*
     * Class:     com_jni_test_ConstructJavaObj
     * Method:    firstConstruct
     * Signature: ()V
     */
    JNIEXPORT void JNICALL Java_com_jni_test_ConstructJavaObj_firstConstruct
      (JNIEnv *env, jobject obj) {
    
        jclass date_clazz = env->FindClass("java/util/Date");
    
        jmethodID date_construct = env->GetMethodID(date_clazz, "<init>", "()V");
    
        jobject date_obj = env->NewObject(date_clazz, date_construct);
    
        jmethodID method_id = env->GetMethodID(date_clazz, "getTime", "()J");
    
        jlong time_current = env->CallLongMethod(date_obj, method_id);
    
        LOGI("current time is :%lld",time_current);
    }
    
    java中调用native方法
    new ConstructJavaObj().firstConstruct();
    Log.e("liuhang", "mainactivity tine is :"+new Date().getTime());
    
    image.png

    <font color="red">最后别忘了,在CMakeLists.txt中添加对应的cpp文件引用</font>

    #生成so动态库
    ADD_LIBRARY(test-lib SHARED test-lib.cpp hello.cpp changeprop.cpp extends.cpp constructobj.cpp)
    
    target_link_libraries(test-lib log)
    

    通过AllocObject(jclass clazz)创建java对象

    用AllocObject函数创建一个java对象,可以根据传入的jclass创建一个java对象,但是状态是非初始化的,在这个对象使用之前,需要用CallNonvirtualVoidMethod来调用该jclass的构造函数,这样做可以延迟构造函数的调用。还是实现上面的功能,打印date.getTime()返回值

    实现secondConstruct本地方法

    /*
     * Class:     com_jni_test_ConstructJavaObj
     * Method:    secondConstruct
     * Signature: ()V
     */
    JNIEXPORT void JNICALL Java_com_jni_test_ConstructJavaObj_secondConstruct
      (JNIEnv *env, jobject obj) {
    
      jclass date_clazz = env->FindClass("java/util/Date");
      jmethodID date_construct = env->GetMethodID(date_clazz, "<init>", "()V");
    
      jobject date_obj = env->AllocObject(date_clazz);
    
      env->CallNonvirtualVoidMethod(date_obj, date_clazz, date_construct);
    
      jmethodID method_id = env->GetMethodID(date_clazz, "getTime", "()J");
    
      jlong time_current = env->CallLongMethod(date_obj, method_id);
    
      LOGI("int secondConstruct current time is :%lld",time_current);
    
    }
    
    
    image.png

    native中操作java字符串

    # 获取字符串长度
    jsize GetStringLength(jstring string)
    
    # 将jstring对象拷贝到const jchar* 指针字符串
    void GetStringRegion(jstring str, jsize start, jsize len, jchar* buf)
    
    #生成一个jstring对象
    jstring NewString(const jchar* unicodeChars, jsize len)
    
    # 将string对象转换成const jcahr* 字符串指针
    const jchar* GetStringChars(jstring string, jboolean* isCopy)
    

    在java中定义一个字符串属性,在C++中将该字符串进行倒序,在从Java中输出

    package com.jni.test;
    
    /**
     * Created by liuhang on 18-7-21.
     */
    
    public class StringJNI {
    
        public String name = "zhangsan";
    
        public native void convertStr();
    
        static {
            System.loadLibrary("test-lib");
        }
    
    }
    
    

    编写convertstr.cpp类

    #include <jni.h>
    #include <string>
    #include <android/log.h>
    #include <algorithm>
    #define  LOG_TAG    "liuhang"
    #define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
    using namespace std;
    
    extern "C"
    
    /*
     * Class:     com_jni_test_StringJNI
     * Method:    convertStr
     * Signature: ()V
     */
    JNIEXPORT void JNICALL Java_com_jni_test_StringJNI_convertStr
      (JNIEnv * env, jobject jobject) {
        jclass jclazz = env->GetObjectClass(jobject);
    
        jfieldID j_name_id = env->GetFieldID(jclazz, "name", "Ljava/lang/String;");
        jstring j_namestr = (jstring)env->GetObjectField(jobject, j_name_id);
    
        const jchar * jstr = env->GetStringChars(j_namestr, NULL);
        wstring wstr((const wchar_t*)jstr);
    
        //释放指针
        env->ReleaseStringChars(j_namestr, jstr);
    
        reverse(wstr.begin(), wstr.end());
    
        jstring j_newstr = env->NewString((const jchar*)wstr.c_str() , (jint)wstr.size());
        env->SetObjectField(jobject, j_name_id, j_newstr);
    
    }
    
    
    

    native操作java中的数组

    操作基本类型数组

    Get<Type>ArrayElements方法
    

    上述函数可以吧java基本类型的数组转换到c/c++中的数组,有两种处理方式:一种是拷贝一份传回本地代码,另一种是把指向java数组的指针直接传回到本地代码中,处理完本地化的数组后,通过Release<Type>ArrayElements来释放数组。

    Release<Type>ArrayElements
    

    用上述方法可以选择将如何处理java和c++的数组,是提交还是撤销等,内存释放还是不释放等。

    void* GetPrimitiveArrayCritical(jarray array, jboolean* isCopy)
    void ReleasePrimitiveArrayCritical(jarray array, void* carray, jint mode)
    
    get<Type>ArrayRegion
    #在c/c++中预先开辟一段内存,然后把java基本类型的数组拷贝到这段内存中
    
    set<Type>ArrayRegion
    #把java基本类型数组中的指定范围的元素用c/c++数组中的元素来赋值
    
    <Type>ArrayNew方法
    指定一个长度然后返回相应的java基本类型的数组
    

    操作对象类型数组

    JNI没有提供把java对象类型数组(Object[])直接转换到c++中的Object[]数组的函数,而是通过Get/SetObjectArrayElement这样的函数来对java的Object[]数组进行操作,由于对象数组没有进行拷贝,所以不需要释放任何资源,NewObjectArray可以通过制定长度和初始值来创建某个类的数组

    创建ArrayTest.java

    package com.jni.test;
    
    /**
     * Created by liuhang on 18-7-21.
     */
    
    public class ArrayTest {
    
        int []arrays = {3, 4, 13, 56, 34, 22,88,21};
        Father[] fathers = {new Father(), new Father(), new Father()};
    
        public native void testArray();
    
        static{
            System.loadLibrary("test-lib");
        }
    }
    
    

    签名如下:

    public class com.jni.test.ArrayTest {
      int[] arrays;
        Signature: [I
      com.jni.test.Father[] fathers;
        Signature: [Lcom/jni/test/Father;
      public com.jni.test.ArrayTest();
        Signature: ()V
    
      public native void testArray();
        Signature: ()V
    
      static {};
        Signature: ()V
    }
    
    

    创建arraytest.cpp

    #include <jni.h>
    #include <string>
    #include <android/log.h>
    #define  LOG_TAG    "liuhang"
    #define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
    
    extern "C"
    
    /*
     * Class:     com_jni_test_ArrayTest
     * Method:    testArray
     * Signature: ()V
     */
    JNIEXPORT void JNICALL Java_com_jni_test_ArrayTest_testArray
      (JNIEnv * env, jobject jobject) {
    
        jclass jclazz = env->GetObjectClass(jobject);
    
        jfieldID arrayid = env->GetFieldID(jclazz, "arrays", "[I");
    
        jintArray arrayobj = (jintArray)env->GetObjectField(jobject, arrayid);
    
        jint* int_array = env->GetIntArrayElements(arrayobj, NULL);
    
        jsize length = env->GetArrayLength(arrayobj);
    
        LOGI("int array length is :%d", length);
        for(int i = 0; i < length; i++) {
            LOGI("int array %d place is :%d",i, int_array[i]);
        }
    }
    
    

    此时执行程序,运行结果如下:


    image.png

    <font color="red">将上述数组中下表偶数位,保存到新的数组里</font>

    // 将上述数组中下表偶数位,保存到新的数组里
        jintArray jsave_array = env->NewIntArray(length);
        jint*  save_array = env->GetIntArrayElements(jsave_array, NULL);
        jint count = 0;
        for(int i = 0; i < length; i++) {
            if (i % 2 == 0) {
                save_array[count++] = int_array[i];
            }
        }
    
        //打印新的数组内容
        for(int j = 0; j < count; j++) {
            LOGI("save_array %d place is :%d",j, save_array[j]);
        }
    
    image.png

    <font color="red">将上述数组中0到3位,拷贝到数组,并且打印出来</font>

    // 将上述数组中0到3位,拷贝到数组,并且打印出来
        jint * jmemory_array = new jint[length];
        env->GetIntArrayRegion(arrayobj, 0, 3, jmemory_array);
    
        for(int j = 0; j < count; j++) {
            LOGI("jmemory_array %d place is :%d",j, save_array[j]);
        }
    
    image.png

    <font color='red'>把上述数组中的3-7位,赋新的值</font>

     // 把上述数组中的3-7位,赋新的值
        jint * replace_array = new jint[4];
        for (int k = 0; k < 4; k++) {
            replace_array[k] = k+1;
        }
        env->SetIntArrayRegion(arrayobj, 3, 4, replace_array);
        // 重新获取数组指针
        int_array = env->GetIntArrayElements(arrayobj, NULL);
        for(int i = 0; i < length; i++) {
           LOGI("int array after replace ... %d place is :%d",i, int_array[i]);
        }
    
    image.png

    <font color='red'>调用sort函数对上述数组进行排序</font>

    #include <algorithm>
    using namespace std;
    
    std :: sort(int_array, int_array+length);
    for(int i = 0; i < length; i++) {
        LOGI("int array after sort ... %d place is :%d",i, int_array[i]);
    }
    
    image.png

    JNIEnv操作引用类型的数组

    <font color='red'>获取上述fathers数组中的第一个Father类对象,并且执行其function方法</font>

    // 获取fathers属性字段id
        jfieldID j_father_id = env->GetFieldID(jclazz, "fathers", "[Lcom/jni/test/Father;");
        // 获取fathers属性
        jobjectArray  j_father_obj = (jobjectArray)env->GetObjectField(jobject, j_father_id);
        // 获取Father类class
        jclass j_father_clazz = env->GetObjectClass(env->GetObjectArrayElement(j_father_obj, 1));
        // 根据Father类class 获取function方法id
        jmethodID j_father_method = env->GetMethodID(j_father_clazz, "function", "()V");
        // 执行function方法
        env->CallVoidMethod(env->GetObjectArrayElement(j_father_obj, 1), j_father_method);
    

    此时打印如下:

    07-21 00:51:36.828  3710  3710 E liuhang : father function runs..... name is :lisi
    

    <font color='red'>创建一个大小为5的Father类型数组,并且值为fathers数组中的第三个元素,执行其function方法</font>

     // 创建一个大小为5的Father类型数组,并且值为fathers数组中的第三个元素
         jobjectArray j_new_obj = env->NewObjectArray(5, j_father_clazz, env->GetObjectArrayElement(j_father_obj, 2));
         // 获取j_new_obj数组中的第四个对象,执行其function方法
         env->CallVoidMethod(env->GetObjectArrayElement(j_new_obj, 3), j_father_method);
    

    此时打印如下:

    07-21 00:51:36.828  3710  3710 E liuhang : father function runs..... name is :wangwu
    

    c/c++中的引用类型

    局部引用

    局部引用是最常见的引用类型,基本上通过JNI返回来的引用都是局部引用,例如:使用NewObject就会返回创建出来的实力的局部引用,局部引用只在该native函数中有效,所有在该函数中产生的局部引用,都会在该函数返回的时候,自动释放,也可以使用DeleteLocalRef函数手动释放该引用。

    全局引用

    全局引用可以跨越当前线程,在多个native函数中有效,不过需要开发人员手动释放该引用,全局引用存在期间会防止在java的垃圾回收器的回收。

    与局部引用不同,全局引用的创建不是由JNI自动创建的,全局引用是需要调用NewGlobalRef函数,而释放它需要使用ReleaseGlobalRef函数

    弱全局引用

    弱全局引用是java 1.2新出来的功能,与全局引用相似,创建和删除都需要开发人员进行,这种引用与全局引用一样,可以在多个本地代码中有效,也跨越多线程有效,不同的是,这种引用将不会阻止垃圾回收器回收这个引用所指向的对象,使用NewWeakGLobalRef和ReleaseWeakGlobalRef来产生和解除引用。

    ID缓存

    缓存jfieldID/jmethodID,取得jfieldID和jmethodID时候,会通过该属性/方法名称加上签名来查询相应的jfieldID/jmethodID。这种查询相对来说开销大,我们可以将这些jfieldID/jmethodID缓存起来,这样需要查询一次,以后就使用缓存起来的jfieldID/jmethodID

    在使用的时候缓存

    在native代码中使用static局部变量保存已经查询过的id,这样就不会在每次函数调用时候查询,而只要第一次查询成功后就保存起来。

    static jfieldID fieldID_str = NULL;
         jclass clazz = env->GetObjectClass(obj);
         if (fieldID_str == NULL) {
            fieldID_str = env->GetFieldID(clazz, "string", "Ljava/lang/String;");
         }
    

    在java类初始化时缓存

    更好的一个方式是在任何native函数调用前把ID全部存起来,可以让java在第一次加载这个类的时候,首先调用本地代码初始化所有的jfieldID/jmethodID

    jfieldID g_propInt_id = 0;
    jfieldID g_propStr_id = 0;
    
    JNIEXPORT void JNICALL Java_com_jni_test_Hello_test
      (JNIEnv * env, jobject obj) {
        g_propInt_id = env->GetFieldID(hello_clazz, "propertyInt", "I");
            g_propStr_id = env->GetFieldID(hello_clazz, "propertyStr", "Ljava/lang/String;");
    }
    

    相关文章

      网友评论

          本文标题:Android NDK开发(第三天)

          本文链接:https://www.haomeiwen.com/subject/ohwlmftx.html