JNI
From JVMLanguages
The Java Native Interface (JNI) is a bridge that can be used to communicate between Java code and C or C++ code. JNI provides a set of header files against which you can compile a shared library (a .so on UNIX, .dll on Windows). This shared library must define functions that have the appropriate names and signatures. Adding the native keyword to a Java methods will replace it with a stub that locates the corresponding C function and wraps all input parameters and the return value with structures defined in JNI's header files.
Implementing Native Methods in Java
To implement a Java method in C, you need to do two things. First, you must declare your method using the keyword native and no implementation.
package com.jvmlanguages.jni;
public class JNITest {
public native int doSomethingNatively (String str, long l);
}
Next, you will need to define a method in C with the appropriate signature. In this case, the name must be Java_com_oreilly_javalangint_jni_JNITest_doSomethingNatively. The easiest way to do this is to use the javah tool included with your Java Developer Kit to generate a stub. javah takes a compiled Java class as input and generates a C header file.
$ javah -classpath . com.jvmlanguages.jni.JNITest
You should now have a file called com_jvmlanguages_jni_JNITest.h that contains the following declarations:
/*
* Class: com_ojvmlanguages_jni_JNITest
* Method: doSomethingNatively
* Signature: (Ljava/lang/String;J)I
*/
JNIEXPORT jint JNICALL Java_com_jvmlanguages_jni_JNITest_doSomethingNatively
(JNIEnv *, jobject, jstring, jlong);
The JNI headers provide Java-specific data types for each of Java's primitive types, and for java.lang.String. Each JNI call also receives two additional arguments: JNIEnv and a this pointer. An instance of JNIEnv can be used to call back into the Java interpreter, and to receive various state information. This is described in more detail below. The jobject that is passed into each JNI call is a pointer to the Java object on which the method was invoked. It can be used to look up fields or call other methods (native or non-native).
Maintaining Peer Objects
It is often useful, especially when using an object-oriented C++ API, to have a one-to-one correspondence between Java classes and C++ classes. In this case, each Java object has a corresponding C++ object, and their lifecycles are usually linked. Each call to a native Java method must have some way to locate the C++ peer object. The way this is usually done is to store a reference to the C++ object on the Java object. However, Java has no concept of pointers, so this value is usually stored on a numeric field in Java. int is sometimes used, although since they are only 32-bits, this will cause problems for 64-bit JVM's. long fields are preferred.
It may be tempting to put a finalize method on your Java classes that free their corresponding C++ peer. This effectively lets you not worry about the memory management for your C++ classes. However, bear in mind that finalization in Java can be extremely expensive. If you are creating a large number of objects, there is a good chance that the finalization thread will fall behind. Objects that declare finalize methods must be seen at least twice during garbage collection before their memory can be reclaimed. This will very likely affect performance. Interacting with Java Objects from C
The JNIEnv pointer that is passed into each JNI call can be used as a starting point for many Java operations that you may wish to perform from native code. In C++, you can use the format:
env->FindClass("java/lang/String")
But in C, you must use the following syntax:
(*env)->FindClass(env, "java/lang/String")
The majority of the functions available on JNIEnv parallel those available through Java's java.lang.Class class and java.lang.reflect package. For example, to locate a class by name, use:
jclass FindClass(const char *name);
From here you can locate and interact with fields and methods:
jmethodID GetStaticMethodID(jclass clazz, const char *name, const char *sig);
jmethodID GetMethodID(jclass clazz, const char *name, const char *sig);
jfieldID GetStaticFieldID(jclass clazz, const char *name, const char *sig);
jfieldID GetFieldID(jclass clazz, const char *name, const char *sig);
void CallVoidMethod(jobject obj, jmethodID methodID, ...);
jobject CallObjectMethod(jobject obj, jmethodID methodID, ...);
j... Call...Method(jobject obj, jmethodID methodID, ...);
void CallNonvirtualVoidMethod(jobject obj, jclass clazz, jmethodID methodID, ...);
jobject CallNonvirtualObjectMethod(jobject obj, jclass clazz, jmethodID methodID, ...);
j... CallNonvirtual...Method(jobject obj, jclass clazz, jmethodID methodID, ...);
jobject GetObjectField(jobject obj, jfieldID fieldID);
j... Get...Field(jobject obj, jfieldID fieldID);
void SetObjectField(jobject obj, jfieldID fieldID, jobject val);
void Set...Field(jobject obj, jfieldID fieldID, j... val);
void CallStaticVoidMethod(jclass cls, jmethodID methodID, ...);
jobject CallStaticObjectMethod(jclass clazz, jmethodID methodID, ...);
j... CallStatic...Method(jclass clazz, jmethodID methodID, ...);
jobject GetStaticObjectField(jclass clazz, jfieldID fieldID);
j... GetStatic...Field(jclass clazz, jfieldID fieldID);
void SetStaticObjectField(jclass clazz, jfieldID fieldID, jobject value);
void SetStatic...Field(jclass clazz, jfieldID fieldID, j... value);
There are also functions available to perform Java-specific features such as throwing exceptions and interacting with strings and arrays.
jint Throw(jthrowable obj);
jint ThrowNew(jclass clazz, const char *msg);
void FatalError(const char *msg);
jstring NewString(const jchar *unicode, jsize len);
jsize GetStringLength(jstring str);
const jchar *GetStringChars(jstring str, jboolean *isCopy);
void GetStringRegion(jstring str, jsize start, jsize len, jchar *buf);
void ReleaseStringChars(jstring str, const jchar *chars);
jstring NewStringUTF(const char *utf);
jsize GetStringUTFLength(jstring str);
const char* GetStringUTFChars(jstring str, jboolean *isCopy);
void GetStringUTFRegion(jstring str, jsize start, jsize len, char *buf);
void ReleaseStringUTFChars(jstring str, const char* chars);
jsize GetArrayLength(jarray array);
jobjectArray NewObjectArray(jsize len, jclass clazz, jobject init);
jbooleanArray NewBooleanArray(jsize len);
j...Array New...Array(jsize len);
jobject GetObjectArrayElement(jobjectArray array, jsize index);
void SetObjectArrayElement(jobjectArray array, jsize index, jobject val);
j... * Get...ArrayElements(j...Array array, jboolean *isCopy);
void Release...ArrayElements(j...Array array, j... *elems, jint mode);
void Get...ArrayRegion(j...Array array, jsize start, jsize len, j... *buf);
void Set...ArrayRegion(j...Array array, jsize start, jsize len, const j... *buf);
There are also functions available to instantiate objects and compare them to other objects.
jobject AllocObject(jclass clazz);
jobject NewObject(jclass clazz, jmethodID methodID, ...);
jclass GetObjectClass(jobject obj);
jclass GetSuperclass(jclass sub);
jboolean IsAssignableFrom(jclass sub, jclass sup);
jboolean IsSameObject(jobject obj1, jobject obj2);
jboolean IsInstanceOf(jobject obj, jclass clazz);

