在我的previous question我在 JNI 调用之间缓存了 JNIEnv*。并来自comment我开始知道它是无效的,这导致我学习 JNI 本地和全局引用。我做了一些测试程序来理解它。从测试程序中我无法理解全局引用的使用。因为 Local 引用本身在多个 JNI 调用之间工作正常。我有 3 个问题来自测试程序
- 渴望知道原因,即本地引用如何缓存并正常工作。
- ArrayList 大小的更改有效,但 String 对象无效
- 为什么要知道缓存的 JNIEnv 如何工作,尽管它无效(在我之前的问题中)。
测试代码如下。
jni 代码:
jclass cls1, cls2, cls3;
jmethodID mth1, mth2, mth3;
jstring str1, str2;
jobject obj1, obj2, obj3;
JNIEXPORT void JNICALL Java_c_wrapperforjavaclass_clsNative_fnFindClass
(JNIEnv *env, jobject obj) {
if (cls1 == NULL || str1 == NULL || obj1 == NULL) {
cout << "Initializing new string object" << endl;
cls1 = env->FindClass("java/lang/String");
mth1 = env->GetMethodID(cls1, "<init>", "(Ljava/lang/String;)V");
str1 = env->NewStringUTF("Apple");
obj1 = env->NewObject(cls1, mth1, str1);
mth1 = env->GetMethodID(cls1, "length", "()I");
long i = (long) env->CallIntMethod(obj1, mth1);
cout << "Length = " << i << endl;
//env->DeleteLocalRef(cls1);
//env->DeleteLocalRef(str1);
//env->DeleteLocalRef(obj1);
} else {
cout << "String Object already initialized" << endl;
mth1 = env->GetMethodID(cls1, "length", "()I");
long i = (long) env->CallIntMethod(obj1, mth1);
cout << "Length = " << i << endl;
}
}
JNIEXPORT void JNICALL Java_c_wrapperforjavaclass_clsNative_fnGetExistingObject__Ljava_lang_String_2
(JNIEnv *env, jobject obj, jstring str) {
if (cls2 == NULL || obj2 == NULL) {
cout << "Initializing from existing string object" << endl;
cls2 = env->GetObjectClass(str);
obj2 = (jobject) env->NewLocalRef(str);
mth2 = env->GetMethodID(cls2, "length", "()I");
long i = (long) env->CallIntMethod(obj2, mth2);
cout << "Length = " << i << endl;
//env->DeleteLocalRef(cls2);
//env->DeleteLocalRef(obj3);
} else {
cout << "Object already initialized" << endl;
mth2 = env->GetMethodID(cls2, "length", "()I");
long i = (long) env->CallIntMethod(obj2, mth2);
cout << "Length = " << i << endl;
}
}
JNIEXPORT void JNICALL Java_c_wrapperforjavaclass_clsNative_fnGetExistingObject__Ljava_util_ArrayList_2
(JNIEnv *env, jobject obj, jobject lst) {
if (cls3 == NULL || obj3 == NULL) {
cout << "Initializing from existing ArrayList object" << endl;
cls3 = env->GetObjectClass(lst);
obj3 = (jobject) env->NewLocalRef(lst);
mth3 = env->GetMethodID(cls3, "size", "()I");
long i = (long) env->CallIntMethod(obj3, mth3);
cout << "Size = " << i << endl;
//env->DeleteLocalRef(cls3);
//env->DeleteLocalRef(obj3);
} else {
cout << "Object already initialized" << endl;
mth3 = env->GetMethodID(cls3, "size", "()I");
long i = (long) env->CallIntMethod(obj3, mth3);
cout << "Length = " << i << endl;
}
}
调用Java代码(测试代码):
a.fnFindClass();
a.fnFindClass();
String str = new String("Android OS");
a.fnGetExistingObject(str);
//Increasing the size
str = new String("Will modified string length get effect");
a.fnGetExistingObject(str);
ArrayList<Integer> al = new ArrayList<>();
al.add(1);al.add(2);al.add(3);
a.fnGetExistingObject(al);
al.add(4);al.add(5); //Increasing the size
a.fnGetExistingObject(al);
测试结果:
Initializing new string object
Length = 5
String Object already initialized
Length = 5
Initializing from existing string object
Length = 10
Object already initialized
Length = 10
Initializing from existing ArrayList object
Size = 3
Object already initialized
Length = 5
提前致谢。
最佳答案
全局引用阻止垃圾收集器删除相关对象。该对象也可能不会被收集,只是因为另一个 java 对象引用了它,或者因为垃圾收集器没有在调用之间的短时间内运行。您不应该依赖于此,而应保留对以后可能需要的任何内容的全局引用。
ArrayList 可以调整大小。字符串是不可变的,并且总是需要为 substring 或append 等函数创建一个新字符串。
我相信保留 JNIEnv * 的问题与线程安全有关。 C++ 代码无法知道两个不同的调用不是来自两个不同的线程。每个环境需要附加到不同的线程。
关于Java JNI编程: Actual usage of the Global References,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32200931/