jni 新手笔记五:JNIEnv & JavaVM

96
DON_1007
2019.03.23 14:37 字数 288

cc++JNIEnv不同
c,指针变量

typedef const struct JNINativeInterface* JNIEnv;

使用的时候 (*pEnv)->GetObjectClass(pEnv,obj);

c++,直接指向结构体

struct _JNIEnv;
struct _JavaVM;
typedef const struct JNINativeInterface* C_JNIEnv;

#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;

使用的时候pEnv->GetObjectClass(obj);

不同线程间的JNIEnv不同,换句话说,就是在线程A中的JNIEnv,在线程B中不能使用。
JavaVM一个进程只有一个,java代码调用c++代码的时候jni给了一个JNIEnv变量,但是如果在c++中新创建了一个子线程C用于处理任务,那么在子线程C中不能使用前面的那个JNIEnv。如果子线程中想要使用JNIEnv,只能通过JavaVM得到一个新的JNIEnv,在子线程中使用。

一、获取JavaVM

1、通过JNIEnv获取JavaVM
JNIEXPORT jstring JNICALL Java_com_don_ndk_NDKTest_getHello
        (JNIEnv *pEnv, jobject obj, jint param) {

    pEnv->GetJavaVM(&javaVM);

    jstring result = pEnv->NewStringUTF(getHello());
    return result;
}
2、实现JNI_OnLoad方法,获取JavaVM

在加载动态链接库的时候,JVM会调用JNI_OnLoad(JavaVM* jvm, void* reserved)(如果定义了该函数),第一个参数会传入JavaVM指针。

JavaVM *javaVM = NULL;

jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    javaVM = vm;
    LOGI("JNI_OnLoad ndk test");
    return JNI_VERSION_1_4;
}

二、通过JavaVM获取JNIEnv

创建子线程执行方法doThreadThing,通过calljstringMethod调用java代码

void threadTest() {
    pthread_create(&pthread, NULL, doThreadThing, NULL);
}
void *doThreadThing(void *data) {
    LOGI("doThreadThing ****************");

    if (javaVM == NULL) {
        LOGW("doThreadThing invalid javaVM");
        return NULL;
    }
    JNIEnv *jniEnv = NULL;
    jint ret = javaVM->GetEnv((void **) &jniEnv, JNI_VERSION_1_4);
    LOGI("doThreadThing ret: %d", ret);

    if (ret == JNI_EDETACHED) {
        if (javaVM->AttachCurrentThread(&jniEnv, NULL) != JNI_OK) {
            LOGW("doThreadThing AttachCurrentThread failed");
            return NULL;
        } else {
            LOGI("doThreadThing AttachCurrentThread success");
        }
    }

    if (jniEnv == NULL) {
        LOGW("doThreadThing invalid jniEnv");
        return NULL;
    } else {
        LOGI("doThreadThing get JNIEnv success");
    }

    calljstringMethod(jniEnv, jniObj);

    javaVM->DetachCurrentThread();
    pthread_exit(&pthread);
}

jniObj是在 Java_com_don_ndk_NDKTest_getHello 方法中保存的jobject变量

pthread_t pthread;
JavaVM *javaVM = NULL;
jobject jniObj = NULL;

JNIEXPORT jstring JNICALL Java_com_don_ndk_NDKTest_getHello
        (JNIEnv *pEnv, jobject obj, jint param) {

    jstring result = pEnv->NewStringUTF(getHello());

    pEnv->GetJavaVM(&javaVM);
    jniObj = pEnv->NewGlobalRef(obj);

    threadTest();
    return result;
Android