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(); }
修改要点
- 移除错误的空值判断:原来先判断
g_env == NULL直接报错,但实际上当线程未附着时GetEnv会返回JNI_EDETACHED且g_env为NULL,这时候应该执行附着操作而不是退出逻辑。 - 使用默认附着参数:把未初始化的
JavaVMAttachArgs替换为NULL,让系统使用默认的线程名称和优先级参数,避免因未初始化变量导致的未知问题。
内容的提问来源于stack exchange,提问作者michpohl
相关产品推荐
相关产品推荐

