• 1. Java Native Interface
  • 2. Agenda Overview JNI with C JNI in Package JNI Types and Data Structures Q&A
  • 3. OverviewWhat is JNI? JNI is the native programming interface for java that is part of JDK. Using JNI you can operate with other applications and libraries written in other language such as C,C++.
  • 4. OverviewWhen should we use the JNI? You want some platform specific information and the standard Java class library may not support the platform-dependent features needed by your application. You have some library application written in other language and you want to use it in your java application. You want Java should interact with some low level programming language. For the sake of execution speed.
  • 5. JNI with CStep 1: Write a Java Class that uses C Codes - HelloJNI.javapublic class HelloJNI { static { // Load native library at runtime hello.dll (Windows) // or libhello.so (Unixes) System.loadLibrary("hello"); } // Declare a native method sayHello() private native void sayHello(); // Test Driver public static void main(String[] args) { // invoke the native method new HelloJNI().sayHello(); } }Compile the "HelloJNI.java" into "HelloJNI.class". > javac HelloJNI.java
  • 6. JNI with C1. Create Java source code with native methods native return type method (arguments); 2. Compile Java source code and obtain the class files 3. Generate C/C++ headers for the native methods; javah gets the info it needs from the class files 4. Write the C/C++ source code for the native method using the function prototype from the generated include file and the typedefs from include/jni.h 5. Compile the C/C++ with the right header files 6. Use the linker to create a dynamic library file 7. Execute a Java program that loads the dynamic library System.loadLibrary("dynamic libary");
  • 7. JNI with CStep 2: Create the C/C++ Header file - HelloJNI.h/* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class HelloJNI */ #ifndef _Included_HelloJNI #define _Included_HelloJNI #ifdef __cplusplus extern "C" { #endif /* * Class: HelloJNI * Method: sayHello * Signature: ()V */ JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *, jobject); #ifdef __cplusplus } #endif #endifRun javah utility on the class file to create a header file for C/C++ programs: > javah HelloJNI The output is HelloJNI.h as follows:
  • 8. JNI with CThe header declares a C function Java_HelloJNI_sayHello as follows: JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *, jobject); The naming convention for C function is Java_{package_and_classname}_{function_name}(JNI arguments). The dot in package name shall be replaced by underscore. The arguments: JNIEnv*: reference to JNI environment, which lets you access all the JNI fucntions. jobject: reference to "this" Java object.
  • 9. JNI with CStep 3: C Implementation - HelloJNI.c#include #include #include "HelloJNI.h" // Implementation of native method sayHello() of HelloJNI class JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *env, jobject thisObj) { printf("Hello World!\n"); return; }The header "jni.h" is available under the "\include" and "\include\win32" (windows) or "\include\linux" (linux) directories, where is your JDK installed directory (e.g., "c:\program files\java\jdk1.7.0"). Windows ENV // Compile HellJNI.c into shared library hello.dll > gcc -Wl,--add-stdcall-alias -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" -shared -o hello.dll HelloJNI.c You can also compile and link in two steps: // Compile-only with -c flag. Output is HelloJNI.o > gcc -c -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" HelloJNI.c // Link into shared library "hello.dll" > gcc -Wl,--add-stdcall-alias -shared -o hello.dll HelloJNI.o
  • 10. JNI with CStep 3: C Implementation - HelloJNI.cLinux ENV > gcc -I "$JAVAHOME/include/linux" -I"$JAVAHOME/include" -fPIC -shared -o libhello.so HelloJNI.c注:和windows下不同,linux的库文件必须是以libxxx.so形式命令的(或者 libxxx.so.y,y是版本号),lib前缀是为了系统能够识别它,xxx是java代码System.loadLibrary("xxx");中引用库的名字。
  • 11. JNI with CStep 4: Run the Java Program> java HelloJNI or > java -Djava.library.path=. HelloJNI specify the library path of the "XXX.dll" via VM option -Djava.library.path=, as shown above. copy XXX.so to java.library.path export LD_LIBRARY_PATH=/home/strade/lib
  • 12. JNI In Packagepackage myjni; public class HelloJNI { static { System.loadLibrary("hello1"); } private native void sayHello(); public static void main(String[] args) { new HelloJNI().sayHello(); } }Step 1: JNI Program - myjni\HelloJNI.java For production, all Java classes shall be kept in proper packages, instead of the default no-name package. This JNI class is kept in package "myjni" - to be saved as "myjni\HelloJNI.java". Compile the JNI program: // change directory to package base directory > javac myjni/HelloJNI.java
  • 13. JNI In PackageStep 2: Generate C/C++ Header // Change directory to package base directory > javah -classpath . -d include myjni.HelloJNI In this example, we decided to place the header file under a "include" sub-directory. The output is "include\myjni_HelloJNI.h". The header file declares a native function: JNIEXPORT void JNICALL Java_myjni_HelloJNI_sayHello(JNIEnv *, jobject);
  • 14. JNI In PackageStep 3: C Implementation - HelloJNI.c Compile the C program: > gcc -Wl,--add-stdcall-alias -I\include -I\include\win32 -shared -o hello1.dll HelloJNI.c You can now run the JNI program: > java -Djava.library.path=. myjni.HelloJNI#include #include #include "include/myjni_HelloJNI.h" JNIEXPORT void JNICALL Java_myjni_HelloJNI_sayHello(JNIEnv *env, jobject thisObj) { printf("Hello World!\n"); return; }
  • 15. JNI Types and Data StructuresJNI defines the following JNI types in the native system that correspond to Java types: 1. Java Primitives: jint, jbyte, jshort, jlong, jfloat, jdouble, jchar, jboolean for Java Primitive of int, byte, short, long, float, double, char and boolean, respectively. 2. Java Reference Types: jobject for java.lang.Object. It also defines the following sub-types: jclass for java.lang.Class. jstring for java.lang.String. jthrowable for java.lang.Throwable. jarray for Java array. Java array is a reference type with eight primitive array and one Object array. Hence, there are eight array of primitives jintArray, jbyteArray, jshortArray, jlongArray, jfloatArray, jdoubleArray, jcharArray and jbooleanArray; and one object array jobjectArray.
  • 16. JNI Types and Data Structures
  • 17. JNI Types and Data StructuresPassing Primitivespublic class TestJNIPrimitive { static { System.loadLibrary("myjni1"); } private native double average(int n1, int n2); public static void main(String args[]) { System.out.println("In Java, the average is " + new TestJNIPrimitive().average(3, 2)); } }#include #include #include "TestJNIPrimitive.h" JNIEXPORT jdouble JNICALL Java_TestJNIPrimitive_average (JNIEnv *env, jobject thisObj, jint n1, jint n2) { printf("In C, the numbers are %d and %d\n", n1, n2); jdouble result = ((jdouble)n1 + n2) / 2.0; return result; }
  • 18. JNI Types and Data StructuresPassing Strings(1)public class TestJNIString { static { System.loadLibrary("myjni2"); } private native String sayHello(String msg); public static void main(String args[]) { String result = new TestJNIString().sayHello("Hello from Java"); System.out.println("In Java, the returned string is: " + result); } }JNI defined a jstring type to represent the Java String. The last argument (of JNI type jstring) is the Java String passed into the C program. The return-type is also jstring. Passing strings is more complicated than passing primitives, as Java's String is an object (reference type), while C-string is a NULL-terminated char array. You need to convert between Java String (represented as JNI jstring) and C-string (char*). To get a C-string (char*) from JNI string (jstring), invoke method const char* GetStringUTFChars(JNIEnv*, jstring, jboolean*). To get a JNI string (jstring) from a C-string (char*), invoke method jstring NewStringUTF(JNIEnv*, char*).
  • 19. JNI Types and Data StructuresPassing Strings(2) #include #include #include "TestJNIString.h" JNIEXPORT jstring JNICALL Java_TestJNIString_sayHello(JNIEnv *env, jobject thisObj, jstring inJNIStr) { // Convert the JNI String (jstring) into C-String (char*) const char *inCStr = (*env)->GetStringUTFChars(env, inJNIStr, NULL); if (NULL == inCStr) return NULL; // Perform its intended operations printf("In C, the received string is: %s\n", inCStr); (*env)->ReleaseStringUTFChars(env, inJNIStr, inCStr); // release resources // Prompt user for a C-string char outCStr[128]; printf("Enter a String: "); scanf("%s", outCStr); // Convert the C-string (char*) into JNI String (jstring) and return return (*env)->NewStringUTF(env, outCStr); }Always invoke ReleaseStringUTFChars() whenever you do not need the returned string of GetStringUTFChars() to release the memory and the reference so that it can be garbage-collected.
  • 20. JNI Types and Data StructuresPassing Array(1)public class TestJNIPrimitiveArray { static { System.loadLibrary("myjni3"); } private native double[] sumAndAverage(int[] numbers); public static void main(String args[]) { int[] numbers = {22, 33, 33}; double[] results = new TestJNIPrimitiveArray().sumAndAverage(numbers); System.out.println("In Java, the sum is " + results[0]); System.out.println("In Java, the average is " + results[1]); } }you need to convert between JNI array and native array, e.g., between jintArray and C's jint[], or jdoubleArray and C's jdouble[]. The JNI Environment interface provides a set of functions for the conversion: To get a C native jint[] from a JNI jintArray, invoke jint* GetIntArrayElements(JNIEnv *env, jintArray a, jboolean *iscopy). To get a JNI jintArray from C native jint[], first, invoke jintArray NewIntArray(JNIEnv *env, jsize len) to allocate, then use void SetIntArrayRegion(JNIEnv *env, jintArray a, jsize start, jsize len, const jint *buf) to copy from the jint[] to jintArray
  • 21. JNI Types and Data StructuresPassing Array(2)#include #include #include "TestJNIPrimitiveArray.h" JNIEXPORT jdoubleArray JNICALL Java_TestJNIPrimitiveArray_sumAndAverage (JNIEnv *env, jobject thisObj, jintArray inJNIArray) { // Convert the incoming JNI jintarray to C's jint[] jint *inCArray = (*env)->GetIntArrayElements(env, inJNIArray, NULL); if (NULL == inCArray) return NULL; jsize length = (*env)->GetArrayLength(env, inJNIArray); // Perform its intended operations jint sum = 0; int i; for (i = 0; i < length; i++) { sum += inCArray[i]; } jdouble average = (jdouble)sum / length; (*env)->ReleaseIntArrayElements(env, inJNIArray, inCArray, 0); // release resources jdouble outCArray[] = {sum, average}; //Convert the C's Native jdouble[] to JNI jdoublearray, and return jdoubleArray outJNIArray = (*env)->NewDoubleArray(env, 2); // allocate if (NULL == outJNIArray) return NULL; (*env)->SetDoubleArrayRegion(env, outJNIArray, 0 , 2, outCArray); // copy return outJNIArray; }
  • 22. JNI Types and Data StructuresAccessing Object's Instance Variables(1)public class TestJNIInstanceVariable { static { System.loadLibrary("myjni"); } private int number = 88; private native void modifyInstanceVariable(); public static void main(String args[]) { TestJNIInstanceVariable test = new TestJNIInstanceVariable(); test.modifyInstanceVariable(); System.out.println("In Java, int is " + test.number); } }include #include #include "TestJNIInstanceVariable.h" JNIEXPORT void JNICALL Java_TestJNIInstanceVariable_modifyInstanceVariable (JNIEnv *env, jobject thisObj) { // Get a reference to this object's class jclass thisClass = (*env)->GetObjectClass(env, thisObj); // Get the Field ID of the instance variables "number" jfieldID fidNumber = (*env)->GetFieldID(env, thisClass, "number", "I"); if (NULL == fidNumber) return; // Get the int given the Field ID jint number = (*env)->GetIntField(env, thisObj, fidNumber); printf("In C, the int is %d\n", number); // Change the variable number = 99; (*env)->SetIntField(env, thisObj, fidNumber, number); }
  • 23. JNI Types and Data StructuresAccessing Object's Instance Variables(1)public class TestJNIInstanceVariable { static { System.loadLibrary("myjni"); } private int number = 88; private native void modifyInstanceVariable(); public static void main(String args[]) { TestJNIInstanceVariable test = new TestJNIInstanceVariable(); test.modifyInstanceVariable(); System.out.println("In Java, int is " + test.number); } }include #include #include "TestJNIInstanceVariable.h" JNIEXPORT void JNICALL Java_TestJNIInstanceVariable_modifyInstanceVariable (JNIEnv *env, jobject thisObj) { // Get a reference to this object's class jclass thisClass = (*env)->GetObjectClass(env, thisObj); // Get the Field ID of the instance variables "number" jfieldID fidNumber = (*env)->GetFieldID(env, thisClass, "number", "I"); if (NULL == fidNumber) return; // Get the int given the Field ID jint number = (*env)->GetIntField(env, thisObj, fidNumber); printf("In C, the int is %d\n", number); // Change the variable number = 99; (*env)->SetIntField(env, thisObj, fidNumber, number); }
  • 24. JNI Types and Data StructuresAccessing Object's Instance VariablesTo access the instance variable of an object: Get a reference to this object's class via GetObjectClass(). Get the Field ID of the instance variable to be accessed via GetFieldID() from the class reference. You need to provide the variable name and its field descriptor (or signature). For a Java class, the field descriptor is in the form of "L;", with dot replaced by forward slash (/), e.g.,, the class descriptor for String is "Ljava/lang/String;". For primitives, use "I" for int, "B" for byte, "S" for short, "J" for long, "F" for float, "D" for double, "C" for char, and "Z" for boolean. For arrays, include a prefix "[", e.g., "[Ljava/lang/Object;" for an array of Object; "[I" for an array of int. Based on the Field ID, retrieve the instance variable via GetObjectField() or GetField() function. To update the instance variable, use the SetObjectField() or SetField() function, providing the Field ID.
  • 25. JNI Types and Data StructuresAccessing Class' Static VariablesThe JNI functions for accessing static variable are: jfieldID GetStaticFieldID(JNIEnv *env, jclass cls, const char *name, const char *sig); // Returns the field ID for a static variable of a class. NativeType GetStaticField(JNIEnv *env, jclass clazz, jfieldID fieldID); void SetStaticField(JNIEnv *env, jclass clazz, jfieldID fieldID, NativeType value); // Get/Set the value of a static variable of a class. includes each of the eight primitive types plus Object.
  • 26. JNI Types and Data StructuresPassing struct to Java(1)public class JNIStruct { public class Foo { protected int len; protected String name; } private static native int foo(Foo fooObj); public static void main(String args[]) { System.loadLibrary("mylib"); Foo foo = new JNIStruct().new Foo(); foo(foo); System.out.println(foo.name); System.out.println(foo.len); } }
  • 27. JNI Types and Data StructuresPassing struct to Java(2)#include #include "JNIStruct_Foo.h" #include "JNIStruct.h" typedef struct Foo { int len; char name[100]; } Foo_t; JNIEXPORT jint JNICALL Java_JNIStruct_foo(JNIEnv *env, jobject obj, jobject fooObj) { Foo_t * bar = malloc(sizeof(Foo_t)); jclass clazz; jfieldID fid; strcpy(bar->name, "Yachun Miao"); bar->len = strlen(bar->name); clazz = (*env)->GetObjectClass(env, fooObj); fid = (*env)->GetFieldID(env, clazz, "len", "I"); (*env)->SetLongField(env, fooObj, fid, bar->len); fid = (*env)->GetFieldID(env, clazz, "name", "Ljava/lang/String;"); jstring name = (*env)->NewStringUTF(env, bar->name); (*env)->SetObjectField(env, fooObj, fid, name); free(bar); return 0; }
  • 28. Referencehttp://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/jniTOC.html http://cs.fit.edu/~ryan/java/language/jni.html http://www.ibm.com/developerworks/cn/java/jnimthds/ http://www3.ntu.edu.sg/home/ehchua/programming/java/JavaNativeInterface.html#zz-2.
  • 29. Q&A