无法在任意上下文中获取 JNIEnv* 值 [英] Unable to get JNIEnv* value in arbitrary context

查看:19
本文介绍了无法在任意上下文中获取 JNIEnv* 值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对 NDK 有疑问.

I have an issue with the NDK.

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

In my JNI_OnLoad method, I cache the JavaVm pointer, the class that called the method, and a method id which I use later on:

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 的指针:

I have a small utility method defined as follows that should get me a pointer to the JNIEnv:

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

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

And finally, I have a callback from an OpenSL ES SLAndroidSimpleBufferQueueItf which I want to handle the recorded audio from a 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");
}

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

Where the context parameter for the callback method only holds a reference to the object that called the native method. It is a self defined struct like this:

typedef struct recorderContext{
    jobject caller;
} recorderContext;

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

However, every time I try to run this, I get the "Could not get JNIEnv*" error message from the callback method.

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

My question basically comes down to this: Why can I get a pointer to the JNIEnv in the JNI_OnLoad method, but not in the recorderCallback, as both use the same Java VM pointer to get the JNIEnv?

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

I need this callback to pass the recorded Audio back up to my Java layer for further processing...

推荐答案

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

Why can I get a pointer to the JNIEnv in the JNI_OnLoad method, but not in the recorderCallback, as both use the same Java VM pointer to get the JNIEnv?

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

Because the callback happens on some native thread, different from the VM thread which loads the library. The JNI implementation maintains a JNIEnv per thread, and puts the pointer in thread-local storage. It is only initialized for native threads which are attached to the VM. You need to call AttachCurrentThread() (or more probably AttachCurrentThreadAsDaemon()) inside the callback to get a JNIEnv pointer valid for that thread. This attaches the thread to the VM on the first call and is a nop thereafter.

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

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

Caveat: This answer assumes proper Java. The problems you're seeing suggest that Dalvik behaves the same as the JVM.

这篇关于无法在任意上下文中获取 JNIEnv* 值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆