【问题标题】:How to obtain JNI interface pointer (JNIEnv *) for asynchronous calls异步调用如何获取JNI接口指针(JNIEnv *)
【发布时间】:2012-10-05 17:25:52
【问题描述】:

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

【问题讨论】:

    标签: java c multithreading java-native-interface


    【解决方案1】:

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

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

    【讨论】:

    • 注意AttachCurrentThread的第二个参数如果你不需要任何特殊设置可以为NULL,如果你不需要任何特殊设置,你应该确保在完成后调用DetachCurrentThread附加到开头(否则你会积累无用的 Thread 对象,这些对象永远不会被 GC'd)。
    • NDK r9 中 AttachCurrentThread 函数的定义发生了变化。这是文档链接。 docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/…
    • 不应该JNIEnv->GetJavaVM接受env作为第一个参数吗?
    • @DenisKniazhev env 基本上第一个参数,因为GetJavaVM 是在env 指针上调用的。
    • @DenisKniazhev 正确。 C 没有类,因此您不能在指针上调用方法。在 C++ 中,JNI 提供了自动传递 env 指针的包装类,但在 C 中,您必须手动传递它。
    【解决方案2】:

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

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

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

    完整示例。请注意,我过去曾在我的blog 上更详细地写过此内容

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

    【讨论】:

    • 这个答案中唯一与该问题相关的部分是GetEnvAttachCurrentThreadDetachCurrentThread,甚至没有解释。
    • 这完全解决了我的问题,但是上面的主要解释很漂亮
    • g_obj = env->NewGlobalRef(obj); -------> 抛出错误:在非结构或联合中请求成员“NewGlobalRef”
    • 自己解决了上述错误。我将文件保存为 .c 但使用的是 c++ 语法。
    • 谢谢!这似乎解决了我的应用程序中一个非常顽固但间歇性的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-07
    • 2011-12-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多