82 votes

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

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

98voto

main-- Points 1694

Vous pouvez obtenir un pointeur sur la JVM ( JavaVM* ) avec JNIEnv->GetJavaVM . Vous pouvez stocker ce pointeur en toute sécurité dans une variable globale. Plus tard, dans le nouveau fil de discussion, vous pourrez soit utiliser AttachCurrentThread pour attacher le nouveau thread à la JVM si vous l'avez créé en C/C++ ou simplement pour l'attacher à la JVM. GetEnv si vous avez créé le thread dans le code Java, ce que je ne suppose pas puisque JNI vous passerait un JNIEnv* 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

86voto

Adam Points 5429

Dans le cadre d'appels synchrones utilisant JNI de Java vers C++, l'"environnement" a déjà été configuré par la JVM, mais dans l'autre sens, à partir d'un thread C++ arbitraire, il peut ne pas l'avoir été.

Vous devez donc suivre les étapes suivantes

  • obtenir le contexte de l'environnement de la JVM en utilisant GetEnv
  • joindre le contexte si nécessaire en utilisant AttachCurrentThread
  • appeler la méthode normalement en utilisant CallVoidMethod
  • détacher en utilisant DetachCurrentThread

Exemple complet. Note J'ai déjà écrit à ce sujet de manière plus détaillée sur mon site Internet blog

JavaVM* g_vm;
env->GetJavaVM(&g_vm);

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();
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X