Como obter o ponteiro da interface JNI (JNIEnv *) para chamadas assíncronas

Eu aprendi que o ponteiro da interface JNI (JNIEnv *) é válido apenas no thread atual. Suponha que eu iniciei um novo thread dentro de um método nativo; como ele pode enviar events de maneira assíncrona para um método Java? Como esse novo segmento não pode ter uma referência de (JNIEnv *). Armazenar uma variável global para (JNIEnv *) aparentemente não funcionará?

Dentro de chamadas síncronas usando JNI de Java para C ++, o “ambiente” já foi configurado pela JVM, entretanto, indo na outra direção de um encadeamento arbitrário de C ++, ele pode não ter sido

Portanto, você precisa seguir estas etapas

  • se apossar do contexto do ambiente JVM usando GetEnv
  • Anexe o contexto, se necessário, usando AttachCurrentThread
  • chame o método como normal usando CallVoidMethod
  • separar usando DetachCurrentThread

Exemplo completo. Note que eu escrevi sobre isso no passado em mais detalhes no meu 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(); } 

Você pode obter um ponteiro para a JVM ( JavaVM* ) com JNIEnv->GetJavaVM . Você pode armazenar com segurança esse ponteiro como uma variável global. Posteriormente, no novo encadeamento, você pode usar AttachCurrentThread para append o novo encadeamento à JVM se você o criou em C / C ++ ou simplesmente GetEnv se criou o encadeamento no código java, o que eu não presumo, já que o JNI passaria a você um JNIEnv* então e você não teria esse 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