java jni so缓存,Android JNI 调用时缓存字段和方法ID示例

本文探讨了在JNI中如何有效地缓存Java方法和字段ID,以提高性能。介绍了使用时缓存(在每次调用时查找并缓存)和初始化时缓存(在类加载时缓存)两种策略。使用时缓存简单但可能在多线程环境下导致多次查找,而初始化时缓存则避免了这个问题,但需要预先知道源码。总结了两种策略的适用场景,并提供了示例代码来说明其实施过程。
摘要由CSDN通过智能技术生成

在 JNI 去调用 Java 的方法和访问字段时,最先要做的操作就是获得对应的类以及对应的方法 id。

事实上,通过 FindClass 、GetFieldID、GetMethodID 去找到对应的信息是很耗时的,如果方法被频繁调用,那么肯定不能每次都去查找对应的信息,有必要将它们缓存起来,在下一次调用时,直接使用缓存内容就好了。

缓存有两种方式,分别是使用时缓存和初始化时缓存。

使用时缓存

使用时缓存,就是在调用时查找一次,然后将它缓存成 static 变量,这样下次调用时就已经被初始化过了。

直到内存释放了,才会缓存失效。

extern "C"

JNIEXPORT void JNICALL

Java_com_glumes_cppso_jnioperations_CacheFieldAndMethodOps_staticCacheField(JNIEnv *env, jobject instance, jobject animal) {

static jfieldID fid = NULL; // 声明为 static 变量进行缓存

// 两种方法都行

// jclass cls = env->GetObjectClass(animal);

jclass cls = env->FindClass("com/glumes/cppso/model/Animal");

jstring jstr;

const char *c_str;

// 从缓存中查找

if (fid == NULL) {

fid = env->GetFieldID(cls, "name", "Ljava/lang/String;");

if (fid == NULL) {

return;

}

} else {

LOGD("field id is cached");

}

jstr = (jstring) env->GetObjectField(animal, fid);

c_str = env->GetStringUTFChars(jstr, NULL);

if (c_str == NULL) {

return;

}

env->ReleaseStringUTFChars(jstr, c_str);

jstr = env->NewStringUTF("new name");

if (jstr == NULL) {

return;

}

env->SetObjectField(animal, fid, jstr);

}

通过声明为 static 变量进行缓存。但这种缓存方式显然有弊端,当多个调用者同时调用时,就会出现缓存多次的情况,并且每次调用时都要检查是否缓存过了。

初始化时缓存

在初始化时缓存,就是在类加载时,进行缓存。当类被加载进内存时,会先调用类的静态代码块,所以可以在类的静态代码块中进行缓存。

比如:

public class CacheFieldAndMethodOps extends BaseOperation {

static {

initCacheMethodId(); // 静态代码块中进行缓存

}

private static native void initCacheMethodId();

}

在静态代码块中,可以将所需要的字段 id 或者方法 id 缓存成全局变量。

具体代码如下:

// 全局变量,作为缓存方法 id

jmethodID InstanceMethodCache;

// 初始化加载时缓存方法 id

extern "C"

JNIEXPORT void JNICALL

Java_com_glumes_cppso_jnioperations_CacheFieldAndMethodOps_initCacheMethodId(JNIEnv *env, jclass type) {

jclass cls = env->FindClass("com/glumes/cppso/model/Animal");

InstanceMethodCache = env->GetMethodID(cls, "getName", "()Ljava/lang/String;");

}

在 JNI 中直接将方法 id 缓存成全局变量了,这样再调用时,就不要再进行一次查找了,并且避免了多个线程同时调用会多次查找的情况。

extern "C"

JNIEXPORT void JNICALL

Java_com_glumes_cppso_jnioperations_CacheFieldAndMethodOps_callCacheMethod(JNIEnv *env, jobject instance, jobject animal) {

jstring name = (jstring) env->CallObjectMethod(animal, InstanceMethodCache);

const char *c_name = env->GetStringUTFChars(name, NULL);

LOGD("call cache method and value is %s", c_name);

}

小结

可以看出,如果不能预先知道方法和字段所在类的源码,那么在使用时缓存比较合理。但如果知道的话,在初始化时缓存优点较多,既避免了每次使用时检查,还避免了在多线程被调用的情况。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值