Android 如何在循环中正确释放jstring?
我的应用程序需要使用jni。逻辑如下:Android 如何在循环中正确释放jstring?,android,android-ndk,java-native-interface,Android,Android Ndk,Java Native Interface,我的应用程序需要使用jni。逻辑如下: void myJniFunc(JNIEnv *env, jclass clazz, jobjectArray items) { int count = 10; struct MyObj *myObjArray = (struct MyObj*)malloc(sizeof(struct MyObj) * count); for (i = 0; i < count; i++) { jobject obj = (*e
void myJniFunc(JNIEnv *env, jclass clazz, jobjectArray items) {
int count = 10;
struct MyObj *myObjArray = (struct MyObj*)malloc(sizeof(struct MyObj) * count);
for (i = 0; i < count; i++) {
jobject obj = (*env)->GetObjectArrayElement(env, items, i);
jfieldID fieldId = ...;
jstring jstr = (*env)->GetObjectField(env, obj, fieldId);
myObjArray[i].name = (*env)->GetStringUTFChars(env, jstr);
(*env)->DeleteLocalRef(env, obj);
// Location A
}
// some code which will use myObjArray
process(count, myObjectArray);
// Location B
}
如果我想正确地释放jstring,我应该怎么做?经过一些实验,我得到了一个答案,但我不确定它是否完全正确。 我在循环中删除了jstring的引用,错误“添加到JNI本地引用表失败(有512个条目)”不再发生
void myJniFunc(JNIEnv *env, jclass clazz, jobjectArray items) {
int count = 10;
jstring tempArray[count];
struct MyObj *myObjArray = (struct MyObj*)malloc(sizeof(struct MyObj) * count);
for (i = 0; i < count; i++) {
jobject obj = (*env)->GetObjectArrayElement(env, items, i);
jfieldID fieldId = ...;
jstring jstr = (*env)->GetObjectField(env, obj, fieldId);
myObjArray[i].name = (*env)->GetStringUTFChars(env, jstr);
(*env)->DeleteLocalRef(env, obj);
// Location A
tempArray[i] = jstr;
(*env)->DeleteLocalRef(jstr);
}
// some code which will use myObjArray
process(count, myObjectArray);
// Location B
for (i = 0; i < count; i++) {
(*env)->ReleaseStringUTFChars(env, tempArray[i], myObjectArray[i].name);
}
}
void myJniFunc(JNIEnv*env、jclass clazz、jobjectArray项){
整数计数=10;
jstring tempArray[count];
struct MyObj*myObjArray=(struct MyObj*)malloc(sizeof(struct MyObj)*count);
对于(i=0;iGetObjectArrayElement(env,items,i);
jfieldID fieldId=。。。;
jstringjstr=(*env)->GetObjectField(env,obj,fieldId);
myObjArray[i].name=(*env)->GetStringUTFChars(env,jstr);
(*env)->DeleteLocalRef(env,obj);
//地点A
tempArray[i]=jstr;
(*env)->DeleteLocalRef(jstr);
}
//一些将使用myObjArray的代码
进程(计数,myObjectArray);
//地点B
对于(i=0;iReleaseStringUTFChars(env,tempArray[i],myObjectArray[i].name);
}
}
我关注的是:
函数“ReleaseStringUTFChars”的第二个参数需要是一个jstring。
所以我创建了一个数组来保存jstring的引用,以供以后发布。
当我在循环中删除jstring的引用时,这意味着jstring被释放。
在这个jstring上有我称之为“ReleaseStringUTFChars”的问题吗?
通过测试,我没有遇到任何问题。因为您的循环正在创建本地引用(GetObjectField),所以需要在循环中释放它(DeleteLocalRef),否则您将遇到本地引用的限制。您必须完全处理两次调用之间的Java字符串 由于您希望将字符串的字节保留在循环之外使用,因此需要复制字节,因为在释放字符串引用之前必须释放JVM的固定(或临时副本)(GetStringUTFChars)(ReleaseStringUTFChars) 因此,循环中字符串的顺序必须是:
GetObjectField
GetStringUTFChars
ReleaseStringUTFChars
DeleteLocalRef
注意:使用
GetStringUTFChars
可以获得一个指向Java字符串的修改UTF-8编码的指针。这里有两点:
GetStringUTFLength
获取修改后的UTF-8编码中的字节数,不计算任何0-终止符。(各种JNI实现都同意阵列是0端接的。)如果您想使用终止符创建自己的副本,请确保为终止符添加空间GetStringChars
和GetStringLength
。在这种情况下,阵列肯定不会终止;它使用内部计数和字符串字节,而不进行任何转换
或者,如果您想更改字符集,例如,真正的“UTF-8”、“ASCII”、“CP437”或“Windows-1252”或您的代码可以处理的其他内容,请使用
字符串.getBytes
重载或字符集
类。如果您想控制如何处理目标字符集中不支持的字符,请使用Charset
类。除了Tom Blodget的帖子之外,我可以添加:不要对jstring对象使用DeleteLocalRef方法,并且不要在第一位(例如,当您从java方法检索字符串时)!将其保存在jobject类型引用中,并且仅当您将其作为param传递时才将其转换为jstring,否则会出现“JNI错误(app bug):accessed stale Local”错误!
例如,不要做这样的事情:
jstring imeiObj = (jstring)env->CallObjectMethod(telephonyManagerObj, jniInfo->jniCache.telephonyManagerClass_getDeviceIdMethod, i);
const char* imeiStr = env->GetStringUTFChars(imeiObj, &isCopy);
env->DeleteLocalRef((jobject)imeiObj);
最好是:
jobject imeiObj = env->CallObjectMethod(telephonyManagerObj, jniInfo->jniCache.telephonyManagerClass_getDeviceIdMethod, i);
const char* imeiStr = env->GetStringUTFChars((jstring)imeiObj, &isCopy);
env->DeleteLocalRef(imeiObj);
我不知道为什么会这样MyObj的“name”字段实际上应该包含一个
jstring
,还是应该包含一个Cchar*
?似乎是后者,在这种情况下,您应该从每个字符串中获取字符,并将它们复制到新分配的char
数组中,然后将它们存储在data
字段中,然后释放Java字符。@Ernest Friedman Hill抱歉,我忘记添加一行调用“GetStringUTFChars”。我再次编辑这个问题。谢谢。这可能就是所谓的“未定义行为”。对字符串的本地引用已被释放,但字符串仍被固定(或已为副本分配内存),然后使用本地引用执行另一个操作。删除后不要使用本地引用。我希望CheckJNI模式拒绝这个调用。当前实现在发布调用中没有使用jstring引用,但这可能会改变。MUTF-8字符串以null结尾。@fadden谢谢。为了保持实用性,我对措辞做了一些修改。
jobject imeiObj = env->CallObjectMethod(telephonyManagerObj, jniInfo->jniCache.telephonyManagerClass_getDeviceIdMethod, i);
const char* imeiStr = env->GetStringUTFChars((jstring)imeiObj, &isCopy);
env->DeleteLocalRef(imeiObj);