web-dev-qa-db-fra.com

Comment obtenir un pointeur d'interface JNI (JNIEnv *) pour les appels asynchrones

J'ai appris que le pointeur d'interface JNI (JNIEnv *) n'est valide que dans le thread actuel. Supposons que je commence un nouveau thread dans une méthode native; comment il peut envoyer de manière asynchrone des événements à une méthode Java? Comme ce nouveau thread ne peut pas avoir de référence (JNIEnv *). Le stockage d'une variable globale pour (JNIEnv *) ne fonctionnera apparemment pas?

67
Akhilesh

Dans les appels synchrones utilisant JNI à partir de Java vers C++, l '"environnement" a déjà été configuré par la machine virtuelle Java, mais allant dans l'autre sens depuis un thread C++ arbitraire, il n'a peut-être pas été

Par conséquent, vous devez suivre ces étapes

  • récupérer le contexte de l'environnement JVM en utilisant GetEnv
  • attachez le contexte si nécessaire en utilisant AttachCurrentThread
  • appelez la méthode normalement en utilisant CallVoidMethod
  • détachez-vous avec DetachCurrentThread

Exemple complet. Notez que j'ai écrit à ce sujet dans le passé plus en détail sur mon 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();
}
69
Adam

Vous pouvez obtenir un pointeur sur la machine virtuelle Java (JavaVM*) avec JNIEnv->GetJavaVM . Vous pouvez stocker ce pointeur en toute sécurité en tant que variable globale. Plus tard, dans le nouveau thread, vous pouvez utiliser AttachCurrentThread pour attacher le nouveau thread à la machine virtuelle Java si vous l'avez créé en C/C++ ou simplement GetEnv si vous avez créé le fil dans Java que je ne présume pas puisque JNI vous passerait un JNIEnv* alors et vous n'auriez pas ce problème.

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