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á?

Author: user207421, 2012-10-15

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();
}
 60
Author: Adam,
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
 72
Author: main--,
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