Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to obtain JNI interface pointer (JNIEnv *) for asynchronous calls

I have learned that the JNI interface pointer (JNIEnv *) is only valid in the current thread. Suppose I started a new thread inside a native method; how it can asynchronously send events to a Java method? As this new thread can't have a reference of (JNIEnv *). Storing a global variable for (JNIEnv *) apparently will not work?

like image 637
Akhilesh Avatar asked Oct 15 '12 17:10

Akhilesh


People also ask

What is JNIEnv * env?

jobject NewGlobalRef(JNIEnv *env, jobject obj); Creates a new global reference to the object referred to by the obj argument. The obj argument may be a global or local reference.

How do I get JavaVM?

You can obtain a pointer to the JVM ( JavaVM* ) with JNIEnv->GetJavaVM . You can safely store that pointer as a global variable.

What JNI calls?

In software design, the Java Native Interface (JNI) is a foreign function interface programming framework that enables Java code running in a Java virtual machine (JVM) to call and be called by native applications (programs specific to a hardware and operating system platform) and libraries written in other languages ...

What is JNI Bridge?

Introduction to Java Native Interface: Establishing a bridge between Java and C/C++ JNI (Java Native Interface) is a foreign function interface that allows code running on JVM to call (or be called by) native applications. Using JNI, one can call methods written in C/C++ or even access assembly language.


2 Answers

You can obtain a pointer to the JVM (JavaVM*) with JNIEnv->GetJavaVM. You can safely store that pointer as a global variable. Later, in the new thread, you can either use AttachCurrentThread to attach the new thread to the JVM if you created it in C/C++ or simply GetEnv if you created the thread in java code which I do not assume since JNI would pass you a JNIEnv* then and you wouldn't have this problem.

    // 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 
like image 92
main-- Avatar answered Sep 28 '22 10:09

main--


Within synchronous calls using JNI from Java to C++ the "environment" has already been setup by the JVM, however going in the other direction from an arbitrary C++ thread it may not have been

Therefore you need to follow these steps

  • get hold of the JVM environment context using GetEnv
  • attach the context if necessary using AttachCurrentThread
  • call the method as normal using CallVoidMethod
  • detach using DetachCurrentThread

Full example. Note I have written about this in the past in more detail on my 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(); } 
like image 45
Adam Avatar answered Sep 28 '22 10:09

Adam