缓存字段和方法ID

来源:互联网 发布:python宝典 pdf 编辑:程序博客网 时间:2024/05/21 22:46

获取字段或方法ID时,需要用字段、方法的名字和描述进行一个检索。检索过程比较耗时,可以通过缓存技术来减少这个过程带来的消耗。
缓存字段ID和方法ID的方法主要有两种。
1.使用时缓存
2.定义字段和方法的类静态初始化时缓存

使用时缓存

字段或方法ID在字段的值被访问或者方法被回调的时候缓存起来。

1.先定义两个native方法

    //缓存字段(使用时)    public native void cacheFieldWhenUse();    //缓存方法ID(使用时)    public native String cacheMethodWhenUse(char[] chars);

2.生成c头文件

JNIEXPORT void JNICALL Java_com_test_git_jnidemo_JniUtil_JniDemo_cacheFieldWhenUse(JNIEnv *, jobject);JNIEXPORT jstring JNICALL Java_com_test_git_jnidemo_JniUtil_JniDemo_cacheMethodWhenUse        (JNIEnv *, jobject, jcharArray);

3.方法实现

3.1缓存字段

下面方法主要实现的是:获取name字段值并修改。

JNIEXPORT void JNICALL Java_com_test_git_jnidemo_JniUtil_JniDemo_cacheFieldWhenUse        (JNIEnv *env, jobject jobj){    //0.静态变量,用于缓存fid    static jfieldID fid = NULL;    //1.获取jclass    jclass cls = (*env).GetObjectClass(jobj);    //2.获取name的fid    if(fid == NULL){        fid = (*env).GetFieldID(cls, "name", "Ljava/lang/String;");        //获取失败        if(fid == NULL){            return; //error        }    }    //3.获取name字段值    jstring jstr = (jstring) (*env).GetObjectField(jobj, fid);    //4.将字符串转换成字符指针    const char *str = (*env).GetStringUTFChars(jstr, NULL);    //4.1如果为NULL,则发生了内存溢出    if(str == NULL){        return; //out of memory    }    //5.将str字符添加到str_new后面    char *c = "缓存字段测试 ";    char new_char[strlen(str) + strlen(c)];    //复制c_str 到 new_char    strcpy(new_char, c);    strcat(new_char, str);    //6.及时释放str    (*env).ReleaseStringUTFChars(jstr, str);    //7.重新生成jstr    jstr = (*env).NewStringUTF(new_char);    //7.1如果为NULL,则发生了内存溢出    if(jstr == NULL){        return; //out of memory    }    //8.修改name字段值    (*env).SetObjectField(jobj, fid, jstr);};

3.2缓存方法ID

下面的方法实现的是将java中传递过来的字符数组转换成字符串返回

JNIEXPORT jstring JNICALL Java_com_test_git_jnidemo_JniUtil_JniDemo_cacheMethodWhenUse        (JNIEnv *env, jobject jobj, jcharArray elemArr){    //1.要缓存的方法ID:cid    static jmethodID cid = NULL;    //2.获取String的jclass    jclass stringClass = (*env).FindClass("java/lang/String");    if(stringClass == NULL){        return NULL; //exception    }    //3.判断cid是否已经初始化    if(cid == NULL){        cid = (*env).GetMethodID(stringClass, "<init>", "([C)V");        if(cid == NULL){            return NULL; //exception        }    }    //4.把传入的字符数组转换成字符串    jstring result = (jstring) (*env).NewObject(stringClass, cid, elemArr);    //5.本地引用引用    /* free local references */    (*env).DeleteLocalRef(elemArr);    (*env).DeleteLocalRef(stringClass);    //6.返回字符串    return result;};

java中调用

        Log.i(TAG, "------------------------------------------");        Log.i(TAG, "name修改前: " + jd.name);        jd.cacheFieldWhenUse();        Log.i(TAG, "name修改后: " + jd.name);        Log.i(TAG, "------------------------------------------");        char[] chars = {'a', 'b', 'c'};        Log.i(TAG, "result传入:{'a', 'b', 'c'}");        String result = jd.cacheMethodWhenUse(chars);        Log.i(TAG, "result输出:" + result);        Log.i(TAG, "------------------------------------------");

输出结果

09-29 12:41:42.431 25020-25020/com.test.git.jnidemo I/MainActivity-: ------------------------------------------09-29 12:41:42.431 25020-25020/com.test.git.jnidemo I/MainActivity-: name修改前: changed Lucy09-29 12:41:42.431 25020-25020/com.test.git.jnidemo I/MainActivity-: name修改后: 缓存字段测试 changed Lucy09-29 12:41:42.431 25020-25020/com.test.git.jnidemo I/MainActivity-: ------------------------------------------09-29 12:41:42.431 25020-25020/com.test.git.jnidemo I/MainActivity-: result传入:{'a', 'b', 'c'}09-29 12:41:42.431 25020-25020/com.test.git.jnidemo I/MainActivity-: result输出:abc09-29 12:41:42.431 25020-25020/com.test.git.jnidemo I/MainActivity-: ------------------------------------------

tips

上面的方法多次调用的时候缓存的字段或方法只会初始化一次,因而提高了效率。

类的静态初始化过程中缓存字段和方法 ID

1.先写两个native方法

    //缓存(初始化时)    public static native void cacheWhenInit();    //使用上面方法的缓存    public native String cacheWhenInitInvoke(char[] chars);

2.生成相应的头文件

JNIEXPORT void JNICALL Java_com_test_git_jnidemo_JniUtil_JniDemo_cacheWhenInit        (JNIEnv *, jclass);JNIEXPORT jstring JNICALL Java_com_test_git_jnidemo_JniUtil_JniDemo_cacheWhenInitInvoke        (JNI

3.方法实现

3.1缓存jmethodID

//缓存的String的String(char value[])方法idjmethodID strInitID;JNIEXPORT void JNICALL Java_com_test_git_jnidemo_JniUtil_JniDemo_cacheWhenInit        (JNIEnv *env, jclass jcls){    //1.获取String的jclass    jclass strClass = (*env).FindClass("java/lang/String");    if(strClass == NULL){        return; //exception    }    //2.判断cid是否已经初始化    if(strInitID == NULL){        strInitID = (*env).GetMethodID(strClass, "<init>", "([C)V");        if(strInitID == NULL){            return; //exception        }    }};

3.2直接使用3.1缓存的ID

//使用上面的strInitIDJNIEXPORT jstring JNICALL Java_com_test_git_jnidemo_JniUtil_JniDemo_cacheWhenInitInvoke        (JNIEnv *env, jobject jobj, jcharArray chars){    jclass stringClass = (*env).FindClass("java/lang/String");    //把传入的字符数组转换成字符串    return (jstring) (*env).NewObject(stringClass, strInitID, chars);};

4.JAVA调用过程

4.1首先初始化时调用

    static {        System.loadLibrary("NdkJniDemo");        cacheWhenInit();    }

4.2在其他地方直接使用

        Log.i(TAG, "------------------------------------------");        Log.i(TAG, "result传入:{'a', 'b', 'c'}");        result = jd.cacheWhenInitInvoke(chars);        Log.i(TAG, "result输出:" + result);        Log.i(TAG, "------------------------------------------");

4.3输出结果:

09-29 12:41:42.431 25020-25020/com.test.git.jnidemo I/MainActivity-: ------------------------------------------09-29 12:41:42.431 25020-25020/com.test.git.jnidemo I/MainActivity-: result传入:{'a', 'b', 'c'}09-29 12:41:42.431 25020-25020/com.test.git.jnidemo I/MainActivity-: result输出:abc09-29 12:41:42.431 25020-25020/com.test.git.jnidemo I/MainActivity-: ------------------------------------------

两种缓存方式对比

如果JNI程序员不能控制方法和字段所在的类的源码的话,在使用时缓存是个合理的方案。

比起静态初始时缓存来说,使用时缓存有一些缺点:
1.使用时缓存的话,每次使用时都要检查一下。
2.方法ID和字段ID在类被unload时就会失效,如果你在使用时缓存ID,你必须确保只要本地依赖于这个ID的值,那么这个类不会被unload。另一方面,如果缓存发生在静态初始化时,当类被unload和reload时,ID会被重新计算。
因此,尽可能在静态初始化时缓存字段ID和方法ID

0 0
原创粉丝点击