javavm - krishnaramb/cplusplus GitHub Wiki

JavaVM and JNIEnv

SOURCE

  • JNI defines two key data structures, "JavaVM" and "JNIEnv". Both of these are essentially pointers to pointers to function tables. (In the C++ version, they're classes with a pointer to a function table and a member function for each JNI function that indirects through the table.)

  • The JavaVM provides the "invocation interface" functions, which allow you to create and destroy a JavaVM. In theory you can have multiple JavaVMs per process, but Android only allows one

  • The JNIEnv provides most of the JNI functions. Your native functions all receive a JNIEnv as the first argument

  • The JNIEnv is used for thread-local storage. For this reason, you cannot share a JNIEnv between threads. If a piece of code has no other way to get its JNIEnv, you should share the JavaVM, and use GetEnv to discover the thread's JNIEnv. (Assuming it has one; see AttachCurrentThread below.)

Threads

  • All threads are Linux threads, scheduled by the kernel. They're usually started from managed code (using Thread.start()), but they can also be created elsewhere and then attached to the

  • For example, a thread started with pthread_create() or std::thread can be attached using the AttachCurrentThread() or AttachCurrentThreadAsDaemon() functions.

  • Until a thread is attached, it has no JNIEnv, and cannot make JNI calls 👊

  • It's usually best to use Thread.start() to create any thread that needs to call in to Java code. Doing so will ensure that you have sufficient stack space, that you're in the correct ThreadGroup, and that you're using the same ClassLoader as your Java code.

java main thread

SOURCE

  • This example uses three functions in the API. The Invocation API allows a native application to use the JNI interface pointer to access VM features. The design is similar to Netscape’s JRI Embedding Interface.

Creating the VM The JNI_CreateJavaVM() function loads and initializes a Java VM and returns a pointer to the JNI interface pointer. The thread that called JNI_CreateJavaVM() is considered to be the main thread.

Attaching to the VM The JNI interface pointer (JNIEnv) is valid only in the current thread. Should another thread need to access the Java VM, it must first call AttachCurrentThread() to attach itself to the VM and obtain a JNI interface pointer. Once attached to the VM, a native thread works just like an ordinary Java thread running inside a native method. The native thread remains attached to the VM until it calls DetachCurrentThread() to detach itself.

The attached thread should have enough stack space to perform a reasonable amount of work. The allocation of stack space per thread is operating system-specific. For example, using pthreads, the stack size can be specified in the pthread_attr_t argument to pthread_create

Calling Java Methods from native

SOURCE
SOURCE

  • This section illustrates how you can call Java methods from native methods. Our example program, Callbacks.java, invokes a native method. The native method then makes a call back to a Java method. To make things a little more interesting, the Java method again (recursively) calls the native method. This process continues until the recursion is five levels deep, at which time the Java method returns without making any more calls to the native method. To help you see this, the Java method and the native method print a sequence of tracing information
JNIEXPORT void JNICALL
Java_Callbacks_nativeMethod(JNIEnv *env, jobject obj, jint depth)
{
  jclass cls = (*env)->GetObjectClass(env, obj);
  jmethodID mid = (*env)->GetMethodID(env, cls, "callback", "(I)V");
  if (mid == 0)
    return;
  printf("In C, depth = %d, about to enter Java\n", depth);
  (*env)->CallVoidMethod(env, obj, mid, depth);
  printf("In C, depth = %d, back from Java\n", depth);
}
  • You can call an instance (non-static) method by following these three steps:
    • Your native method calls GetObjectClass. This returns the Java class object to which the Java object belongs.
    • Your native method then calls GetMethodID. This performs a lookup for the Java method in a given class. The lookup is based on the name of the method as well as the method signature. If the method does not exist, GetMethodID returns 0. An immediate return from the native method at that point causes a NoSuchMethodError to be thrown in Java code.
    • Lastly, your native method calls CallVoidMethod. This invokes an instance method that has void return type. You pass the object, method ID, and the actual arguments to CallVoidMethod.