3
votes

I have an Android application which consists of some native threads (not attached to JVM) which need to be able to call methods of a Java object.

The way in which I was intending to do this was to create a JNI function which I call from the relevant Java object which allows me to obtain and cache the required java object method ID's, JNIEnv and object references in a static native data structure so that my native threads can (thread safely) access the required methods (e.g. using (*env)->CallVoidMethod(env, this, JavaMethodID, ...), etc;

I'm not convinced this approach is going to work, since I read that the JNIEnv pointer can't be shared between threads, and that only threads which are attached to the JVM can do this kind of thing...

Is this a viable approach?

1
JNIEnv is valid only for the current thread, but JavaVM is global. So, save a pointer to that, as well as global references to your classes, objects, method IDs, and that will work.Samuel Audet

1 Answers

5
votes
  1. in JNI_OnLoad, cache JavaVM*. That's the only thing persistent and valid across threads.
  2. as soon as you set up some native thread, call AttachCurrentThread and obtain JNIEnv*, which is valid only for that single thread.
  3. with JavaVM* and JNIEnv*, look up your jclasses, jobjects and jmethodIDs. These are still valid only for the single thread you have attached to.
  4. convert jclasses and jobjects to global references, so that it persists across threads. jmethodIDs do not need to be globalized, they are not jobjects.
  5. On any further native threads, you again need to call AttachCurrentThread to obtain a valid JNIEnv* for that thread.
  6. Don't forget to delete the created global references when you don't need them anymore (in JNI_OnUnload at the latest)