Unable to get JNIEnv* value in arbitrary context

一曲冷凌霜 提交于 2020-01-21 02:15:06

问题


I have an issue with the NDK.

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;
}

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;
}

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;

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

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?

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


回答1:


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?

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.

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

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



来源:https://stackoverflow.com/questions/5991615/unable-to-get-jnienv-value-in-arbitrary-context

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!