java - Cómo obtener el puntero de interfaz JNI(JNIEnv*) para llamadas asincrónicas
multithreading (2)
Aprendí que el puntero de interfaz JNI (JNIEnv *) solo es válido en el hilo actual. Supongamos que comencé un nuevo hilo 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 *). ¿Guardar una variable global para (JNIEnv *) aparentemente no funcionará?
Dentro de llamadas sincrónicas usando JNI de Java a C ++, el "entorno" ya ha sido configurado por la JVM, sin embargo, yendo en la otra dirección desde un hilo de C ++ arbitrario, puede no haber sido
Por lo tanto, debes seguir estos pasos
- obtener el contexto del entorno JVM usando
GetEnv
- adjunte el contexto si es necesario usando
AttachCurrentThread
- llame al método de forma normal utilizando
CallVoidMethod
- separar usando
DetachCurrentThread
Ejemplo completo Tenga en cuenta que 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();
}
Puede obtener un puntero a la JVM ( JavaVM*
) con JNIEnv->GetJavaVM
. Puede almacenar ese puntero de forma segura como una variable global. Más tarde, en el nuevo hilo, puede usar AttachCurrentThread
para adjuntar el nuevo hilo a la JVM si lo creó en C / C ++ o simplemente GetEnv
si creó el hilo en código java que no asumo porque JNI le pasaría una 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