繁体   English   中英

C++ 回调到 Java -> 为什么我不能在来自不同线程的回调 class 中检索 JNIEnv?

[英]C++ Callback to Java -> Why can't I retrieve JNIEnv in callback class when coming from a different thread?

我正在尝试构建一个回调 class 以在 Android 上从我的本机代码中的不同线程调用 Java 方法。 我已经阅读了很多关于如何做到这一点的内容,只要我在同一个线程上,一切都会奏效。 但是从另一个线程我无法正确检索JNIEnv ,而且我无法弄清楚我做错了什么。

我对 C++ 和 JNI 不是很有经验,所以这很可能是一些初学者的问题......但我已经花了几天时间,看不到它是什么。

这是我的回调 class、.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");

        //This is a test call to see if I can call my java method. It works.
        g_env->CallVoidMethod(g_object, id, (jint) 103);
    }
}

// this method is calles from other threads, so I want to attach to the current thread once I got my JNIEnv, but I can't since it's null...
void AudioCallback::playBackProgress(int progressPercentage) {
    JNIEnv *g_env;

    // This is null and I don't know why!
    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);
        //thread gets detached elsewhere
    }
}

这是我的 native_lib,我在其中获取JavaVM并实例化回调 class:

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>(*gJvm, myJNIClass);

    // this test call to my callback works
    callback->playBackProgress(104);

    const char *uri = env->GetStringUTFChars(URI, NULL);

    //... urelated code is left out here ...

    //audioEngine gets the callback and uses it from threads it creates
    audioEngine = std::make_unique<AudioEngine>(*extractor, *callback);
    audioEngine->setFileName(uri);
    audioEngine->start();
}

我已经缩短了代码并删除了所有不相关/不必要的部分。 如果缺少重要的东西,请发表评论,我会添加它。

解决方案:根据@Michael 在他的回答中提出的建议,我在我的回调 class 中对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();
}

现在直接检查getEnvStat的值,之前对getEnvStatg_env检查是错误的。 我还必须用JavaVMAttachArgs替换NULL才能使其工作。

您在playBackProgress中的逻辑错误。

您尝试附加当前线程的唯一时间是g_env为非 NULL 时。 但是如果g_env不为 NULL,那么GetEnv可能会成功(您当然还应该检查getEnvStat == JNI_OK )并且不需要AttachCurrentThread

您需要调用AttachCurrentThread的情况是g_env是 NULL 并且getEnvStatJNI_EDETACHED

您还需要跟踪您是否确实调用了AttachCurrentThread ,因为在这些情况下您应该在某个时候调用DetachCurrentThread 有关更多信息,请参阅此答案

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM