Як отримати вказівник інтерфейсу JNI (JNIEnv *) для асинхронних викликів


75

Я дізнався, що покажчик інтерфейсу JNI (JNIEnv *) дійсний лише в поточному потоці. Припустимо, я розпочав новий потік всередині власного методу; як він може асинхронно надсилати події до методу Java? Оскільки цей новий потік не може мати посилання на (JNIEnv *). Зберігання глобальної змінної для (JNIEnv *), мабуть, не буде працювати?

Відповіді:


78

У рамках синхронних викликів з використанням JNI з Java на C ++ "середовище" вже було встановлено JVM, проте, рухаючись в іншому напрямку від довільного потоку C ++, можливо, це не було

Тому вам потрібно виконати ці кроки

  • дістати контекст середовища JVM за допомогою GetEnv
  • додайте контекст, якщо потрібно, використовуючи AttachCurrentThread
  • викликати метод як звичайно, використовуючи CallVoidMethod
  • від'єднати, використовуючи DetachCurrentThread

Повний приклад. Примітка. Я писав про це раніше в своєму блозі

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

13
Єдині фрагменти цієї відповіді, пов’язані з питанням, є GetEnv, AttachCurrentThreadі DetachCurrentThreadнавіть не пояснюються.
main

1
Це повністю вирішує мою проблему, однак наведене вище пояснення від main було прекрасним
Ахілеш

g_obj = env-> NewGlobalRef (obj); -------> помилка підкидання: Запит на член 'NewGlobalRef' у чомусь, що не є структурою чи об'єднанням
Matical

вирішив вищезазначену помилку сам. Я зберег файл як .c, але використовував синтаксис c ++.
Matical

Дякую! Здається, це виправило дуже вперту, але періодичну проблему в моєму додатку.
Алан Кіннаман,

89

Ви можете отримати вказівник на JVM ( JavaVM*) за допомогою JNIEnv->GetJavaVM. Ви можете безпечно зберігати цей вказівник як глобальну змінну. Пізніше, в новому потоці, ви можете або використовувати AttachCurrentThreadдля приєднання нового потоку до JVM, якщо ви створили його в C / C ++, або просто GetEnvякщо ви створили потік у коді Java, що я не припускаю, оскільки JNI передасть вам JNIEnv*тоді і у вас не було б цієї проблеми.


16
Зверніть увагу, що другим аргументом AttachCurrentThreadможе бути NULL, якщо вам не потрібні ніякі спеціальні налаштування, і вам слід обов’язково зателефонувати, DetachCurrentThreadколи закінчите, якщо для початку не були приєднані (інакше ви накопичите непотрібні Threadоб’єкти, які можуть ніколи не бути GC'd).
technomage

визначення функції AttachCurrentThread змінюється в NDK r9. ось посилання на документ. docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/…
Зефір,

Не слід JNIEnv->GetJavaVMприймати envяк перший параметр?
Денис Княжев

@DenisKniazhev в envосновному є першим параметром, оскільки GetJavaVMвикликається envвказівником.
main

2
@DenisKniazhev Правильно. C не має класів, тому ви не можете викликати метод на покажчику. У C ++ JNI надає класи обгортки, які автоматично передають покажчик env, але в C потрібно передавати його вручну.
main
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.