80

我了解到JNI接口指针(JNIEnv *)只在当前线程中有效。假设我在本机方法中启动了一个新线程;它如何将事件异步发送到 Java 方法?因为这个新线程不能引用(JNIEnv *)。为 (JNIEnv *) 存储全局变量显然不起作用?

4

2 回答 2

97

您可以使用 获取指向 JVM( JavaVM*)的指针JNIEnv->GetJavaVM。您可以安全地将该指针存储为全局变量。AttachCurrentThread稍后,在新线程中,如果您在 C/C++ 中创建新线程,或者只是在 java 代码中创建线程,您可以使用将新线程附加到 JVM GetEnv,我不认为这是因为 JNI 会传递给您一个JNIEnv*then 和你不会有这个问题。

    // JNIEnv* env; (initialized somewhere else)
    JavaVM* jvm;
    env->GetJavaVM(&jvm);
    // now you can store jvm somewhere

    // in the new thread:
    JNIEnv* myNewEnv;
    JavaVMAttachArgs args;
    args.version = JNI_VERSION_1_6; // choose your JNI version
    args.name = NULL; // you might want to give the java thread a name
    args.group = NULL; // you might want to assign the java thread to a ThreadGroup
    jvm->AttachCurrentThread((void**)&myNewEnv, &args);
    // And now you can use myNewEnv
于 2012-10-15T17:35:48.250 回答
86

在使用 JNI 从 Java 到 C++ 的同步调用中,“环境”已经由 JVM 设置,但是从任意 C++ 线程向另一个方向前进,它可能还没有

因此,您需要按照以下步骤操作

  • 使用获取 JVM 环境上下文GetEnv
  • 如有必要,使用附加上下文AttachCurrentThread
  • 使用正常调用该方法CallVoidMethod
  • 使用分离DetachCurrentThread

完整的例子。注意我过去在我的博客上更详细地写过这个

JavaVM* g_vm;
env->GetJavaVM(&g_vm);

void callback(int val) {
    JNIEnv * g_env;
    // double check it's all ok
    int getEnvStat = g_vm->GetEnv((void **)&g_env, JNI_VERSION_1_6);
    if (getEnvStat == JNI_EDETACHED) {
        std::cout << "GetEnv: not attached" << std::endl;
        if (g_vm->AttachCurrentThread((void **) &g_env, NULL) != 0) {
            std::cout << "Failed to attach" << std::endl;
        }
    } else if (getEnvStat == JNI_OK) {
        //
    } else if (getEnvStat == JNI_EVERSION) {
        std::cout << "GetEnv: version not supported" << std::endl;
    }

    g_env->CallVoidMethod(g_obj, g_mid, val);

    if (g_env->ExceptionCheck()) {
        g_env->ExceptionDescribe();
    }

    g_vm->DetachCurrentThread();
}
于 2012-10-15T17:47:39.157 回答