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

Android平台C++跨线程回调Java时JNIEnv获取失败问题

Android JNI跨线程回调Java方法时JNIEnv获取失败的问题解决

问题场景

我在Android原生开发中写了一个C++回调类,用来在不同线程里调用Java层的方法。同线程调用完全没问题,但跨线程的时候死活拿不到正确的JNIEnv,查了好几天资料都没搞定。先贴一下我原来的代码:

原Callback类(.h和.cpp)

class AudioCallback {
public:
    explicit AudioCallback(JavaVM&, jobject);
    void playBackProgress(int progressPercentage);
private:
    JavaVM& g_jvm;
    jobject g_object;
};
jclass target = NULL;
jmethodID id = NULL;
AudioCallback::AudioCallback(JavaVM &jvm, jobject object) : g_jvm(jvm), g_object(object) {
    JNIEnv *g_env;
    int getEnvStat = g_jvm.GetEnv((void **) &g_env, JNI_VERSION_1_6);
    if (g_env != NULL) {
        target = g_env->GetObjectClass(g_object);
        id = g_env->GetMethodID(target, "integerCallback", "(I)V");
        // 同线程测试调用,正常工作
        g_env->CallVoidMethod(g_object, id, (jint) 103);
    }
}
// 这个方法会被其他线程调用,尝试获取JNIEnv并附着线程,但g_env一直为null
void AudioCallback::playBackProgress(int progressPercentage) {
    JNIEnv *g_env;
    // 这里g_env总是null,不知道为什么!
    int getEnvStat = g_jvm.GetEnv((void **) &g_env, JNI_VERSION_1_6);
    if (g_env == NULL) {
        LOGE("JNIEnv in callback method is null");
    } else {
        LOGD("Env Stat: %d", getEnvStat);
        JavaVMAttachArgs vmAttachArgs;
        if (getEnvStat == JNI_EDETACHED) {
            LOGD("GetEnv: not attached - attaching");
            if (g_jvm.AttachCurrentThread(&g_env, &vmAttachArgs) != 0) {
                LOGD("GetEnv: Failed to attach");
            }
        } else if (getEnvStat == JNI_OK) {
            LOGD("GetEnv: JNI_OK");
        } else if (getEnvStat == JNI_EVERSION) {
            LOGD("GetEnv: version not supported");
        }
        g_env->CallVoidMethod(g_object, id, (jint) progressPercentage);
        // 线程在其他地方detach
    }
}

原native_lib代码

std::unique_ptr<AudioEngine> audioEngine;
std::unique_ptr<AudioCallback> callback;
JavaVM *g_jvm = nullptr;
static jobject myJNIClass;
jint JNI_OnLoad(JavaVM *pJvm, void *reserved) {
    g_jvm = pJvm;
    return JNI_VERSION_1_6;
}
JNIEXPORT void JNICALL Java_com_my_appy_common_jni_JniBridge_playFromJNI(JNIEnv *env, jobject instance,jstring URI) {
    myJNIClass = env->NewGlobalRef(instance);
    callback = std::make_unique<AudioCallback>(*g_jvm, myJNIClass);
    // 同线程测试调用回调,正常工作
    callback->playBackProgress(104);
    const char *uri = env->GetStringUTFChars(URI, NULL);
    // 无关代码省略
    // audioEngine会拿到callback并在自己创建的线程中调用
    audioEngine = std::make_unique<AudioEngine>(*extractor, *callback);
    audioEngine->setFileName(uri);
    audioEngine->start();
}

问题解决

后来按照@Michael的建议修改了playBackProgress方法,问题直接解决了!修改后的代码如下:

void AudioCallback::playBackProgress(int progressPercentage) {
    JNIEnv *g_env;
    int getEnvStat = g_jvm.GetEnv((void **) &g_env, JNI_VERSION_1_6);
    if (getEnvStat == JNI_EDETACHED) {
        LOGD("GetEnv: not attached - attaching");
        if (g_jvm.AttachCurrentThread(&g_env, NULL) != 0) {
            LOGD("GetEnv: Failed to attach");
        }
    } else if (getEnvStat == JNI_OK) {
        LOGD("GetEnv: JNI_OK");
    } else if (getEnvStat == JNI_EVERSION) {
        LOGD("GetEnv: version not supported");
    }
    g_env->CallVoidMethod(g_object, id, (jint) progressPercentage);
    // mJvm.DetachCurrentThread();
}

修改要点

  1. 移除错误的空值判断:原来先判断g_env == NULL直接报错,但实际上当线程未附着时GetEnv会返回JNI_EDETACHED且g_env为NULL,这时候应该执行附着操作而不是退出逻辑。
  2. 使用默认附着参数:把未初始化的JavaVMAttachArgs替换为NULL,让系统使用默认的线程名称和优先级参数,避免因未初始化变量导致的未知问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 12:08:16