2011-05-13 8 views
4

NDK에 문제가 있습니다. 임의의 컨텍스트에서 JNIEnv * 값을 가져올 수 없습니다.

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

내가 그해야 다음과 같이 작은 유틸리티 메소드가 정의되어 내주는 JNI_OnLoad 방법에서

, 난 나중에 사용의 JavaVM 포인터, 메서드를 호출 한 클래스 및 메소드 ID를 캐시

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

을 그리고 마지막으로, 나는 내가 SLRecordItf에서 녹음 된 오디오 처리하려는 OpenSL ES SLAndroidSimpleBufferQueueItf에서 콜백이 : JNIEnv의 나에게 포인터를 얻을

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 매개 변수는 원시 메소드를 호출 한 객체에 대한 참조 만 보유합니다.

typedef struct recorderContext{ 
    jobject caller; 
} recorderContext; 

그러나, 나는이를 실행하려고 할 때마다, 나는 콜백 메서드에서 "Could not get JNIEnv*" 오류 메시지가 : 그것은이 같은 자기 정의 구조체이다.

기본적으로이 질문에 대한 나의 질문 : JNI_OnLoad 메서드에서 JNIEnv에 대한 포인터를 얻을 수 있지만 recorderCallback에서는 JNIEnv를 가져 오는 데 동일한 Java VM 포인터를 사용하므로 포인터를 얻을 수없는 이유는 무엇입니까?

내가 아니라 을에, 내가주는 JNI_OnLoad 방법 JNIEnv의 에 대한 포인터를 얻을 수 있습니다 왜 ... 추가 처리를 위해 내 자바 층에 백업

답변

9

를 기록 오디오를 전달하는이 콜백이 필요합니다 recorderCallback은 모두 동일한 Java VM 포인터를 사용하여 JNIEnv를 얻으려고합니까?

콜백은 일부 원시 스레드에서 발생하기 때문에 라이브러리를로드하는 VM 스레드와 다릅니다. JNI 구현은 스레드 당 JNIEnv을 유지하고 스레드 로컬 저장소에 포인터를 배치합니다. VM에 접속되고있는 네이티브 thread 전용으로 초기화됩니다. 콜백 내에 AttachCurrentThread() (또는 더 많은 경우 AttachCurrentThreadAsDaemon())으로 전화하여 해당 스레드에 대해 JNIEnv 포인터가 유효해야합니다. 이것은 첫 번째 호출에서 스레드를 VM에 연결하고 그 이후의 nop입니다.이 답변 적절한 자바를 가정

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

주의해야 참조하십시오. Dalvik은 JVM과 동일한 동작을하는 것이 좋습니다.

+0

감사합니다.이 기능은 ... – ThaMe90

+0

당신의 답변도 저에게 도움이되었습니다. 하나의 VM 스레드에서 JNIEnv ptr을 초기화했으며 ptr을 다른 VM 스레드에서 사용할 수 있다고 생각했습니다. 그러나 ptr을 여러 번 사용한 후에 SIGSEGV를 받았습니다. – alehro

+0

나도 도와 줬어. 그러나 심볼이 내 .so에 있긴하지만 JNI_Onload()가 호출되지 않거나 발견되지 않았다는 사실을 알게되었습니다. C++에서오고 있기 때문에 extern "C"안에 JNI_Onload()를 추가했습니다. –

관련 문제