【问题标题】:Unable to get JNIEnv* value in arbitrary context无法在任意上下文中获取 JNIEnv* 值
【发布时间】:2011-08-24 21:34:04
【问题描述】:

我对 NDK 有疑问。

在我的 JNI_OnLoad 方法中,我缓存了 JavaVm 指针、调用该方法的类以及稍后使用的方法 ID:

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved){
    JNIEnv *env;
    cachedJVM = jvm;
    if((*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_6)){
        LOG_ERROR("Could not get JNIEnv*");
        return JNI_ERR;
    }
    javaClass = (*env)->FindClass(env, "org/test/opensl/AudioProcessor");
    if(javaClass == NULL){
        LOG_ERROR("Could not get java class");
        return JNI_ERR;
    }
    javaCallbackMID = (*env)->GetMethodID(env, javaClass, "enqueueAudio", "([B)V");
    if(javaCallbackMID == NULL){
        LOG_ERROR("Could not get method identifier");
        return JNI_ERR;
    }
    return JNI_VERSION_1_6;
}

我有一个如下定义的小实用程序方法,它应该让我得到一个指向 JNIEnv 的指针:

JNIEnv* JNU_GetEnv(){
    JNIEnv* env;
    (*cachedJVM)->GetEnv(cachedJVM, (void**)&env, JNI_VERSION_1_6);
    return env;
}

最后,我有一个来自 OpenSL ES SLAndroidSimpleBufferQueueItf 的回调,我想处理来自 SLRecordItf 的录制音频:

void recorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context){
    SLresult result;
    JNIEnv* env;
    recorderContext* thisContext = (recorderContext*)context;
    env = JNU_GetEnv();
    if(env == NULL){
        LOG_ERROR("Could not get JNIEnv*");
        return;
    }
    jbyteArray data = (*env)->NewByteArray(env, MAX_PACKET_SIZE);
    if(data == NULL){
        LOG_ERROR("No memory could be allocated for buffer");
        return;
    }
    (*env)->SetByteArrayRegion(env, data, 0, MAX_PACKET_SIZE, recorderBuffer);
    (*env)->CallByteMethodA(env, thisContext->caller, javaCallbackMID, data);
    (*env)->DeleteLocalRef(env, data);
    result = (*bq)->Enqueue(bq, recorderBuffer,
                            RECORDER_FRAMES * sizeof(jbyte));
    checkError(result, "Unable to enqueue new buffer");
}

回调方法的上下文参数只保存对调用本机方法的对象的引用。它是一个像这样的自定义结构:

typedef struct recorderContext{
    jobject caller;
} recorderContext;

但是,每次我尝试运行它时,我都会从回调方法中收到 "Could not get JNIEnv*" 错误消息。

我的问题基本上归结为:为什么我可以在 JNI_OnLoad 方法中获取指向 JNIEnv 的指针,但在 recorderCallback 中却不能,因为两者都使用相同的 Java VM 指针来获取 JNIEnv?

我需要此回调将录制的音频传回我的 Java 层以进行进一步处理...

【问题讨论】:

    标签: android c java-native-interface android-ndk


    【解决方案1】:

    为什么我可以获得指向 JNIEnv 的指针 在 JNI_OnLoad 方法中,但不在 recorderCallback,因为两者都使用 相同的 Java VM 指针来获取 JNIEnv?

    因为回调发生在某个本地线程上,不同于加载库的 VM 线程。 JNI 实现为每个线程维护一个JNIEnv,并将指针放在线程本地存储中。它仅针对附加到 VM 的本机线程进行初始化。您需要在回调中调用AttachCurrentThread()(或者更可能是AttachCurrentThreadAsDaemon())以获得对该线程有效的JNIEnv 指针。这会在第一次调用时将线程附加到 VM,然后是 nop。

    http://download.oracle.com/javase/1.5.0/docs/guide/jni/spec/invocation.html

    警告: 这个答案假设是正确的 Java。您看到的问题表明 Dalvik 的行为与 JVM 相同。

    【讨论】:

    • 您的回答对我也有帮助。我在一个 VM 线程中初始化了 JNIEnv ptr,并认为该 ptr 可以在另一个 VM 线程中使用。但是在多次使用 ptr 后,我收到了 SIGSEGV。
    • 也帮助了我。但是我得到了 JNI_Onload() 没有被调用(甚至找不到)的问题,尽管该符号在我的 .so 中。因为我来自 c++,所以我在 extern "C" 中添加了 JNI_Onload()。
    • 另请参阅developer.android.com/guide/practices/jni.html,其中以粗体显示“您不能在线程之间共享 JNIEnv”。
    猜你喜欢
    • 2019-12-20
    • 2020-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-02
    • 1970-01-01
    相关资源
    最近更新 更多