我对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");
}
其中,回调方法的context参数仅包含对称为 native 方法的对象的引用。这是一个自定义的结构,如下所示:
typedef struct recorderContext{
jobject caller;
} recorderContext;
但是,每次尝试运行此命令时,都会从回调方法中收到
"Could not get JNIEnv*"
错误消息。我的问题基本上可以归结为:为什么我可以在JNI_OnLoad方法中获得指向JNIEnv的指针,而在recorderCallback中却不能,因为两者都使用相同的Java VM指针来获取JNIEnv?
我需要此回调将记录的音频回传到我的Java层以进行进一步处理...
最佳答案
因为回调发生在某些 native 线程上,所以与加载库的VM线程不同。 JNI实现为每个线程维护一个JNIEnv
,并将指针放在线程本地存储中。它仅针对连接到VM的 native 线程初始化。您需要在回调内部调用AttachCurrentThread()
(或更可能是AttachCurrentThreadAsDaemon()
)来获取对该线程有效的JNIEnv
指针。这在第一个调用时将线程附加到VM,此后是nop。
参见http://download.oracle.com/javase/1.5.0/docs/guide/jni/spec/invocation.html
警告:此答案假定使用正确的Java。您看到的问题表明Dalvik的行为与JVM相同。