Come ottenere il puntatore all’interfaccia JNI (JNIEnv *) per le chiamate asincrone

Ho appreso che il puntatore all’interfaccia JNI (JNIEnv *) è valido solo nel thread corrente. Supponiamo di aver iniziato una nuova discussione all’interno di un metodo nativo; come può inviare in modo asincrono eventi a un metodo Java? Poiché questo nuovo thread non può avere un riferimento di (JNIEnv *). L’archiviazione di una variabile globale per (JNIEnv *) apparentemente non funzionerà?

Nelle chiamate sincrone che utilizzano JNI da Java a C ++, l’ambiente è già stato impostato dalla JVM, ma andando nella direzione opposta da un thread C ++ arbitrario potrebbe non essere stato

Pertanto è necessario seguire questi passaggi

  • ottenere il contesto dell’ambiente JVM utilizzando GetEnv
  • albind il contesto se necessario utilizzando AttachCurrentThread
  • chiamare il metodo normalmente usando CallVoidMethod
  • staccare usando DetachCurrentThread

Esempio completo Nota che ho scritto su questo in passato in modo più dettagliato sul mio 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(); } 

È ansible ottenere un puntatore alla JVM ( JavaVM* ) con JNIEnv->GetJavaVM . È ansible archiviare in modo sicuro quel puntatore come variabile globale. Successivamente, nel nuovo thread, puoi utilizzare AttachCurrentThread per colbind il nuovo thread alla JVM se lo hai creato in C / C ++ o semplicemente GetEnv se hai creato il thread in codice java che non presumo dal momento che JNI ti avrebbe passato un JNIEnv* allora e tu non avresti questo problema.

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