The JNI interface pointer is only valid in the current thread. A native method, therefore, must not pass the interface pointer from one thread to another. A VM implementing the JNI may allocate and store thread-local data in the area pointed to by the JNI interface pointer.
Design Overview Invocation API
- Add a "native" keyword before the method declaration in class declaration
- Use
javah
to build C-style header files (javah automatically includes this file in the application header files) - Implement the method in C/C++ code
- Compile the native method into a dynamically linked library
- Load generated library in Java code
- Call native method
A code snippet
/* 1: in java */
public native void sayHello();
/* 2: header generated by javah, com_test_TestNative.h */
/* Method'name always starts with Java */
JNIEXPORT void JNICALL Java_com_test_TestNative_sayHello (JNIEnv *, jobject);
/* 3: in c++ */
#include"com_test_TestNative.h"
...
JNIEXPORT void JNICALL Java_com_test_TestNative_sayHello(JNIEnv *env, jobject obj) {
...
}
/* 4: compile to dynamically linked library (platform dependent) */
gcc -o libNativeCode.so -lc -shared \
-I/usr/local/jdk1.6.0_03/include \
-I/usr/local/jdk1.6.0_03/include/linux com_test_TestNative_sayHello.c
/* in java */
public static void main(String[] args) {
/* 5: load library */
System.loadLibrary("NativeCode");
TestNative tNative = new TestNative();
/* 6: call native method */
tNative.sayHello();
}
An example from the JNI Invocation API
#include <jni.h> /* where everything is defined */
...
JavaVM *jvm; /* denotes a Java VM */
JNIEnv *env; /* pointer to native method interface */
JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
JavaVMOption* options = new JavaVMOption[1];
options[0].optionString = "-Djava.class.path=/usr/lib/java";
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = false;
/* load and initialize a Java VM, return a JNI interface
* pointer in env */
JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
delete options;
/* invoke the Main.test method using the JNI */
jclass cls = env->FindClass("Main");
jmethodID mid = env->GetStaticMethodID(cls, "test", "(I)V");
env->CallStaticVoidMethod(cls, mid, 100);
/* We are done. */
jvm->DestroyJavaVM();
- JNIEnv *env: Is a pointer that points to another pointer pointing to a function table (array of pointer). Each entry in this function table points to a JNI function. These are the functions we are going to use for type conversion
- The second argument is different depending on whether the native method is a static method or an instance method
- Instance method: It will be a jobject argument which is a reference to the object on which the method is invoked
- Static method: It will be a jclass argument which is a reference to the class in which the method is define
Lowercase "jclass" is a type defined in JNI specification. Uppercase "JClass" is a representation of C++ class in Magnolia VM.
Some jni structures are defined as opaque structures to hide definition, there are a couple of ways to define an opaque structure. HotSpot defines them in the following fashion:
// foo.h
typedef struct _foo * fooRef;
void doStuff(fooRef f);
// foo.c
struct _foo {
int x;
int y;
};
The implementation (and usage) of JNI interface has a slight difference depending on the native language being used.
-
C
- (*env)->method(env, args)
- (*vm)->method(vm, args)
-
C++
- env->method(args)
- vm->method(args)
The C++'s style is compatible with C style by placing the pointers as the first member is C++ struct.
In header file:
struct _JNIEnv;
struct _JavaVM;
#if defined(__cplusplus)
typedef _JNIEnv JNIEnv; // C++, the very first member of JNIEnv is JNINativeInterface, makes it compatible with C style definition
typedef _JavaVM JavaVM; // C++
#else
typedef const struct JNINativeInterface* JNIEnv; // C
typedef const struct JNIInvokeInterface* JavaVM; // C
#endif
Refer to JNI.h for the actual defintion of JNINativeInterface, etc.
In C, jclass is a void* while in C++ jclass is an empty class.
#ifdef __cplusplus
/*Reference types, in C++*/
class _jobject {};
class _jclass : public _jobject {};
typedef _jclass* jclass;
#else
/*Reference types, in C.*/
typedef void* jobject;
typedef jobject jclass;
#endif
Note that the jclass returned from a native call cannot be reused.