You need to enable JavaScript to run this app. JNI回调调用Java方法时CallObjectMethod返回NULL问题求助
优惠活动
大模型
产品
解决方案
定价
更多

JNI回调调用Java方法时CallObjectMethod返回NULL问题求助

问题根因

  1. 回调对象未转为全局引用:JNI函数入参中的callbackObject属于局部引用,xx_LoadFileFromPath函数返回后局部引用就会被JVM回收,你直接将局部引用赋值给全局变量g_callbackObject,后续调用时访问的是已经失效的野引用,直接导致方法调用失败。
  2. 异常未正确处理:你已经检测到ExceptionCheck返回1,说明CallObjectMethod调用时已经抛出异常,此时返回值res为NULL,后续直接调用GetStringUTFChars属于错上加错,没有先排查异常具体内容。
  3. 线程附着资源泄漏:如果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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.20 09:45:07