web-dev-qa-db-ja.com

非同期呼び出しのJNIインターフェイスポインター(JNIEnv *)を取得する方法

JNIインターフェイスポインター(JNIEnv *)は現在のスレッドでのみ有効であることを学びました。ネイティブメソッド内で新しいスレッドを開始したとします。非同期にイベントをJavaメソッドに送信できますか?この新しいスレッドは(JNIEnv *)の参照を持つことができません。

67
Akhilesh

JavaからC++へのJNIを使​​用する同期呼び出し内で、「環境」はすでにJVMによってセットアップされていますが、任意のC++スレッドから他の方向に進むことはできませんでした。

したがって、次の手順に従う必要があります

  • GetEnvを使用してJVM環境コンテキストを取得します
  • 必要に応じて、AttachCurrentThreadを使用してコンテキストを添付します
  • CallVoidMethodを使用して通常どおりメソッドを呼び出します
  • DetachCurrentThreadを使用してデタッチします

完全な例。注:これについては、過去に詳細に blog で書いています。

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

JVMへのポインターを取得できます(JavaVM*)with JNIEnv->GetJavaVM 。そのポインターをグローバル変数として安全に保存できます。後で、新しいスレッドで AttachCurrentThread を使用して、新しいスレッドをC/C++で作成した場合はJVMにアタッチするか、単に 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
79
main--