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();
}
Why does the JNI C API use a pointer-to-pointer instead of straight pointers for JNIEnv?
JNIEnv
is not really a pointer to a pointer, but to a data structure containing other (private) thread-specific information. The JNINativeInterface*
is just the first field in the struct, and the rest aren't public. This allows for more flexibility in VM's implementation of JNI function tables.
Some links here for the benefit of those who might come across this:
Threads and JNI - here it explains:
The JNI interface pointer (JNIEnv *) is only valid in the current thread. You must not pass the interface pointer from one thread to another, or cache an interface pointer and use it in multiple threads. The Java Virtual Machine will pass you the same interface pointer in consecutive invocations of a native method from the same thread, but different threads pass different interface pointers to native methods.
JNI spec
Does JNI really need interface pointer to provide multiple versions of JNI function tables
No, they (Sun) could have come up with some other design. This JniEnv is with us for historical reasons, and does contribute to JNI overhead, albeit not significant.
The idea was to let Java side control whether to use debugging set of functions at runtime per thread. I don't believe this idea has ever helped somebody debugging their native code.
Note that the C++ wrapper reduces the hassle significantly (sure, this is syntax only; the overhead doesn't go).
How do I get a JavaVM or JNIEnv from an already-running JVM using JNI?
With the help of the comments, I got that crash to stop happening. The trick was to pre-allocate an array.
let jvm_ptr = Vec::with_capacity(1).as_mut_ptr();
let count = null_mut();
JNI_GetCreatedJavaVMs(jvm_ptr, 1, count);
JNI: Does C++ calls Java asynchronous?
CallVoidMethod
is executed synchronously.
Maybe you have an excepion on c++ side?, do you use c++ jni exception checks?:
env->CallVoidMethod(javaClassObject, javaReceiveMethod, intParam, byteParam, objectParam);
if(env->ExceptionOccurred()) {
// Print exception caused by CallVoidMethod
env->ExceptionDescribe();
env->ExceptionClear();
}
The C++ code (in a shared object library) is running in it's own C++ thread. It is using a existing JavaVM of a java app that is already up and running.
its not clear whether you have attached current thread to virtual machine. Make sure env
is comming from AttachCurrentThread
call. You will find example here: How to obtain JNI interface pointer (JNIEnv *) for asynchronous calls.
Is calling AttachCurrentThread on JNI costly?
Although you CAN attach the calling thread on a per-JNI-call basic, you really SHOULD NOT, unless you have no other choice. A native thread MUST attach itself to the JVM before it can make JNI calls. To avoid unnecessary overhead, the thread really should attach itself as soon as possible (at least before the 1st JNI call), and stay attached until it no longer needs to make any further JNI calls.
JNI IMU code not compiling base operand of ‘-’ has non-pointer type ‘JNIEnv’
JNIEnv
is declared a bit different for C and C++, see jni.h
for details. In few words in C we use
(*env)->NewStringUTF(env,imuData.temperature);
In C++ it should be
env->NewStringUTF(imuData.temperature);
As you see C++ version doesn't need env
dereference and passing it as first parameter to JNI functions.
Related Topics
How to Set Request Encoding in Tomcat
Compiler Error When Declaring a Variable Inside If Condition and No Curly Braces
Delete Item from Array and Shrink Array
Retrieving a List from a Java.Util.Stream.Stream in Java 8
How to Get the Filename Without the Extension in Java
In Log4J, Does Checking Isdebugenabled Before Logging Improve Performance
Validate Jaxbelement in JPA/Jax-Rs Web Service
How to Use @Id with String Type in JPA/Hibernate
Use Custom Fonts When Creating PDF Using Ireport
How to Make Image Appear Randomly Every X Seconds in Java Using Timer
Updating Version Numbers of Modules in a Multi-Module Maven Project
Is Null Check Needed Before Calling Instanceof
Manipulating and Comparing Floating Points in Java
How to Get a Reference Address
How Does Java's System.Exit() Work with Try/Catch/Finally Blocks