Unable to get JNIEnv * value in arbitrary context

I have a problem with NDK.

In my JNI_OnLoad method, I cache the JavaVm pointer, the class that called the method, and the method identifier that I use later:

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 little utility method defined as follows, which should get me a pointer to JNIEnv:

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

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

If the context parameter for the callback method contains only a reference to the object that called its own method. This is a self-defined structure:

 typedef struct recorderContext{ jobject caller; } recorderContext; 

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

My question basically boils down to the following: why can I get a pointer to JNIEnv in the JNI_OnLoad method, but not in recorderCallback, since both use the same Java VM pointer to get JNIEnv?

I need this callback to transfer the recorded audio back to my Java level for further processing ...

+4
source share
1 answer

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

Because the callback happens in some kind of native thread other than the VM thread that loads the library. The JNI implementation supports JNIEnv per stream and places the pointer in the local stream store. It is initialized only for native threads connected to the virtual machine. You must call AttachCurrentThread() (or, more likely, AttachCurrentThreadAsDaemon() ) inside the callback to get the JNIEnv pointer valid for this thread. This attaches the thread to the virtual machine on the first call and after that becomes nop.

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

Caution: this answer assumes proper Java. The problems you see show that Dalvik behaves the same as the JVM.

+9
source

All Articles