How to obtain JNI interface pointer (JNIEnv *) for asynchronous calls
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();
}
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