Cómo obtener el puntero de interfaz JNI (JNIEnv*) para llamadas asíncronas
He aprendido que el puntero de interfaz JNI (JNIEnv *) solo es válido en el hilo actual. Supongamos que inicié un subproceso nuevo dentro de un método nativo; ¿cómo puede enviar eventos de forma asíncrona a un método Java? Como este nuevo hilo no puede tener una referencia de (JNIEnv *). Almacenar una variable global para (JNIEnv *) aparentemente no funcionará?
2 answers
Dentro de las llamadas síncronas que usan JNI de Java a C++, el" entorno " ya ha sido configurado por la JVM, sin embargo, yendo en la otra dirección desde un subproceso arbitrario de C++, puede que no haya sido
Por lo tanto, debe seguir estos pasos
- obtenga el contexto del entorno JVM usando
GetEnv
- adjunte el contexto si es necesario usando
AttachCurrentThread
- llame al método como normal usando
CallVoidMethod
- separar usando
DetachCurrentThread
Ejemplo Completo. Nota He escrito sobre esto en el pasado con más detalle en mi 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();
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-21 05:18:18
Puede obtener un puntero a la JVM (JavaVM*
) con JNIEnv->GetJavaVM
. Puede almacenar de forma segura ese puntero como una variable global. Más tarde, en el nuevo hilo, puede usar AttachCurrentThread
para adjuntar el nuevo subproceso a la JVM si lo creó en C / C++ o simplemente GetEnv
si creaste el subproceso en código java que no asumo ya que JNI te pasaría un JNIEnv*
entonces y no tendrías este 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-15 17:43:04