You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android JNI如何在C++独立线程中调用MainActivity对象的Java方法

错误根本原因

  • 你直接存储的jobjecte(也就是MainActivity实例)是局部引用,JNI方法Java_com_myname_mydetails_myfunction_MainActivity_startItUp执行结束后,这个局部引用就会被JVM自动回收,后续子线程访问的就是已经失效的引用,自然会报invalid jobject错误
  • 额外提醒:不要跨线程存储JNIEnv指针,JNIEnv是线程绑定的,只在所属线程有效,跨线程使用必报错,你子线程已经通过Attach拿到了当前线程的Env,完全不需要提前存储全局Env

解决步骤

1. 将MainActivity的局部引用转为全局引用存储

你在JNI入口方法里拿到MainActivity的jobject之后,调用env->NewGlobalRef把它转为全局引用再存到C++对象里,全局引用只有你手动调用DeleteGlobalRef才会被回收,跨线程访问是安全的。
修改你的JNI入口方法:

extern "C" JNIEXPORT jstring JNICALL
Java_com_myname_mydetails_myfunction_MainActivity_startItUp(
    JNIEnv* env,
    _jobject *jobjecte /* this */) {

    jint err = env->GetJavaVM(&jvm);
    // 把局部引用转为全局引用再传递
    jobject globalMainActivity = env->NewGlobalRef(jobjecte);
    startThings(jvm, globalMainActivity);
    // 不需要传递Env,子线程自己Attach获取即可

    return env->NewStringUTF(hello.c_str());
}

然后修改setJVM方法,去掉存储Env的逻辑:

void setJVM( JavaVM *_jvm, _jobject *_mainActivityGlobal){
    jvm = _jvm;
    mainActivity = _mainActivityGlobal; // 存储的是全局引用
}

2. 子线程中正常使用全局引用即可

你现有的子线程逻辑不用大改,只要保证存储的是全局引用就不会报错:

void myclass::mymethod(int64_t _profile){
    JNIEnv *env = NULL;
    JavaVMAttachArgs args;
    args.version = JNI_VERSION_1_6;
    args.name = NULL;
    args.group = NULL;
    jvm->AttachCurrentThread(&env, &args);
    
    // 这里用全局的mainActivity就不会报错了
    jclass clazz = env->GetObjectClass(mainActivity);
    // 接下来找方法ID、调用方法的逻辑正常写即可,比如你要回传字符串:
    jmethodID updateUiMethod = env->GetMethodID(clazz, "updateUiText", "(Ljava/lang/String;)V");
    jstring content = env->NewStringUTF("你要回传的字符串");
    env->CallVoidMethod(mainActivity, updateUiMethod, content);
    
    // 注意:如果这个线程是一次性的,用完要执行Detach释放资源
    jvm->DetachCurrentThread();
}

3. 规避内存泄漏

当MainActivity销毁的时候(比如用户退出APP),你要主动调用JNI方法释放全局引用,避免内存泄漏:

extern "C" JNIEXPORT void JNICALL
Java_com_myname_mydetails_myfunction_MainActivity_releaseRef(
    JNIEnv* env,
    _jobject *thiz) {
    if (mainActivity != NULL) {
        env->DeleteGlobalRef(mainActivity);
        mainActivity = NULL;
    }
}

然后在MainActivity的onDestroy生命周期里调用这个releaseRef方法即可。

额外注意点

如果要在子线程触发的Java方法里更新界面控件,Java侧的updateUiText方法里要记得用runOnUiThread切换到主线程操作,否则会触发Android子线程无法更新UI的错误。

内容的提问来源于stack exchange,提问作者confused

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 13:45:03