JNI回调调用Java方法时CallObjectMethod返回NULL问题求助
问题根因
- 回调对象未转为全局引用:JNI函数入参中的
callbackObject属于局部引用,xx_LoadFileFromPath函数返回后局部引用就会被JVM回收,你直接将局部引用赋值给全局变量g_callbackObject,后续调用时访问的是已经失效的野引用,直接导致方法调用失败。 - 异常未正确处理:你已经检测到
ExceptionCheck返回1,说明CallObjectMethod调用时已经抛出异常,此时返回值res为NULL,后续直接调用GetStringUTFChars属于错上加错,没有先排查异常具体内容。 - 线程附着资源泄漏:如果
GetJniEnv中触发了AttachCurrentThread,调用完成后没有对应执行DetachCurrentThread,会造成线程资源泄漏,部分平台会直接触发崩溃。
修复方案
1. 回调对象转全局引用
修改xx_LoadFileFromPath中存储回调对象的逻辑:
JNIEXPORT jboolean JNICALL xx_LoadFileFromPath (JNIEnv* env, jobject o, jobject callbackObject) { env->GetJavaVM(&g_vm); jint version = env->GetVersion(); jclass callBackClass = env->GetObjectClass(callbackObject); jboolean check = env->ExceptionCheck(); jmethodID callBackMethode = env->GetMethodID(callBackClass, "CallbackTest", "(Ljava/lang/String;)Ljava/lang/String;"); check = env->ExceptionCheck(); // 局部引用转全局引用,避免被回收 g_callbackObject = env->NewGlobalRef(callbackObject); g_callbackMethod = callBackMethode; bool res = LoadFileFromPath(LoadFileFromPathCallbackWrapper); return res; }
注意:回调不再使用时,需要调用env->DeleteGlobalRef(g_callbackObject)释放全局引用,避免内存泄漏。
2. 回调逻辑增加异常处理和资源释放
char* LoadFileFromPathCallbackWrapper(char* fileName) { JNIEnv* env = nullptr; bool needDetach = GetJniEnv(g_vm, &env); // 拿JNIEnv失败直接返回 if (!env) { return nullptr; } jstring param = env->NewStringUTF(fileName); jstring res = (jstring)env->CallObjectMethod(g_callbackObject, g_callbackMethod, param); // 先检查调用异常 if (env->ExceptionCheck()) { env->ExceptionDescribe(); // 打印异常日志,可定位具体错误(比如引用失效、方法找不到等) env->ExceptionClear(); if (needDetach) { g_vm->DetachCurrentThread(); } return nullptr; } // 正常处理返回值 const char* nativeStr = env->GetStringUTFChars(res, NULL); char* result = nullptr; if (nativeStr) { #ifdef OS_LINUX result = strdup(nativeStr); #else result = _strdup(nativeStr); #endif } env->ReleaseStringUTFChars(res, nativeStr); // 释放局部引用 env->DeleteLocalRef(param); env->DeleteLocalRef(res); // 附着的线程调用完成后要解绑 if (needDetach) { g_vm->DetachCurrentThread(); } return result; }
额外检查点
- 确认
CallbackTest方法是public修饰,没有被混淆工具移除或者重命名 - 确认方法签名完全匹配,没有多余空格、大小写错误
内容的提问来源于stack exchange,提问作者TheValbo
相关产品推荐
相关产品推荐

