缓存字段和方法ID

获取字段或方法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 Lucy
09-29 12:41:42.431 25020-25020/com.test.git.jnidemo I/MainActivity-: name修改后: 缓存字段测试 changed Lucy
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输出:abc
09-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[])方法id
jmethodID 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

//使用上面的strInitID
JNIEXPORT 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输出:abc
09-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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值