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

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

问题描述

我有一个问题,NDK。

在我JNI_OnLoad方法,我缓存JavaVM的指针,调用该方法的类和方法ID,我后来就用:

  JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM的* JVM,无效*保留){
    JNIEnv的* ENV;
    cachedJVM = JVM;
    如果((* JVM) - > GETENV(JVM,(无效**)及包膜,JNI_VERSION_1_6)){
        LOG_ERROR(无法获取JNIEnv的*);
        返回JNI_ERR;
    }
    一个JavaClass =(* ENV) - >的findClass(ENV,组织/测试/ opensl / SC7314音频处理器);
    如果(一个JavaClass == NULL){
        LOG_ERROR(无法获取Java类);
        返回JNI_ERR;
    }
    javaCallbackMID =(*的env) - >的GetMethodID(ENV,JavaClass中,enqueueAudio,(〔B)V);
    如果(javaCallbackMID == NULL){
        LOG_ERROR(无法获取方法标识符);
        返回JNI_ERR;
    }
    返回JNI_VERSION_1_6;
}
 

我有一个小工具,方法如下定义,应该让我一个指向JNIEnv的:

 的JNIEnv * JNU_GetEnv(){
    JNIEnv的* ENV;
    (* cachedJVM) - > GETENV(cachedJVM,(无效**)及包膜,JNI_VERSION_1_6);
    返回ENV;
}
 

最后,我有一个从OpenSL ES SLAndroidSimpleBufferQueueItf ,我想处理录制的音频从 SLRecordItf

 无效recorderCallback(SLAndroidSimpleBufferQueueItf BQ,无效*上下文){
    SLresult结果;
    JNIEnv的* ENV;
    recorderContext * thisContext =(recorderContext *)范围内;
    的env = JNU_GetEnv();
    如果(ENV == NULL){
        LOG_ERROR(无法获取JNIEnv的*);
        返回;
    }
    jbyteArray数据=(* ENV) - > NewByteArray(ENV,MAX_PACKET_SIZE);
    如果(数据== NULL){
        LOG_ERROR(无内存可分配的缓冲区);
        返回;
    }
    (* ENV) - > SetByteArrayRegion(ENV,数据,0,MAX_PACKET_SIZE,recorderBuffer);
    (* ENV) - > CallByteMethodA(ENV,thisContext->来电,javaCallbackMID,数据);
    (* ENV) - > DeleteLocalRef(ENV,数据);
    结果=(* BQ) - >排队(BQ,recorderBuffer,
                            RECORDER_FRAMES *的sizeof(jbyte));
    checkError(因此,无法排队新的缓冲区);
}
 

如果回调方法的上下文参数只持有引用调用本地方法的对象。它是这样一个自定义的结构:

  typedef结构recorderContext {
    jobject来电;
} recorderContext;
 

不过,每次我尝试运行此,我从回调方法无法获取的JNIEnv *的错误消息。

我的问题基本上可以归结为这样:我为什么能得到一个指向JNIEnv的在JNI_OnLoad方法,但不是在recorderCallback,因为两者使用相同的Java虚拟机的指针来获取JNIEnv的

我需要这个回调录制的音频回传上到我的Java层进行进一步的处理...

解决方案
  

为什么我可以得到一个指向JNIEnv的   在JNI_OnLoad方法,但不是在   的recorderCallback,既使用   相同的Java虚拟机的指针,以获得   JNIEnv的?

由于回调发生在一些本地线程,从虚拟机线程,加载库的不同。 JNI的实现保持一个的JNIEnv 每个线程,并把指针线程本地存储。它仅初始化为,它们分别连接到VM本地线程。你需要调用 AttachCurrentThread()(更可能是 AttachCurrentThreadAsDaemon())回调内获得的JNIEnv 指针有效的线程。这种高度线程虚拟机上的第一个电话,是一个NOP之后。

请参阅<一href="http://download.oracle.com/javase/1.5.0/docs/guide/jni/spec/invocation.html">http://download.oracle.com/javase/1.5.0/docs/guide/jni/spec/invocation.html

警告:的这个回答假设正确的Java。你看到的问题表明,Dalvik的行为等同于JVM。

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...

解决方案

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.

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

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