Java Native Interface
From Wikipedia, the free encyclopedia
The Java Native Interface (JNI) is a programming framework that allows Java code running in a Java Virtual Machine (JVM) to call and to be called[1] by native applications (programs specific to a hardware and operating system platform) and libraries written in other languages, such as C, C++ and assembly.
Contents |
[edit] Purpose and features
JNI allows one to write native methods to handle situations when an application cannot be written entirely in the Java programming language e.g. when the standard Java class library does not support the platform-specific features or program library. It is also used to modify an existing application, written in another programming language, to be accessible to Java applications. Many of the standard library classes depend on JNI to provide functionality to the developer and the user, e.g. I/O file reading and sound capabilities. Including performance- and platform-sensitive API implementations in the standard library allows all Java applications to access this functionality in a safe and platform-independent manner. Before resorting to using JNI, developers should make sure the functionality is not already provided in the standard libraries.
The JNI framework lets a native method utilize Java objects in the same way that Java code uses these objects. A native method can create Java objects and then inspect and use these objects to perform its tasks. A native method can also inspect and use objects created by Java application code.
JNI is sometimes referred to as the "escape hatch" for Java developers because it allows them to add functionality to their Java application that the standard Java APIs cannot otherwise provide. It can be used to interface with code written in other languages, such as C and C++. It is also used for time-critical calculations or operations like solving complicated mathematical equations, since native code can be faster than JVM code.
[edit] Pitfalls
- subtle errors in the use of JNI can destabilize the entire JVM in ways that are very difficult to reproduce and debug;
- only applications and signed applets can invoke JNI;
- an application that relies on JNI loses the platform portability Java offers (a workaround is to write a separate implementation of JNI code for each platform and have Java detect the operating system and load the correct one at runtime);
- the JNI framework does not provide any automatic garbage collection for non-JVM memory resources allocated by code executing on the native side. Consequently, native side code (such as C, C++, or assembly language) must assume the responsibility for explicitly releasing any such memory resources that it itself acquires;
- error checking is a MUST or it has the potential to crash the JNI side and the JVM.
- on Linux and Solaris platforms, if the native code registers itself as a signal handler it could intercept signals intended for the JVM. Signal chaining should be used to allow native code to better interoperate with JVM.[2]
- on Windows platforms Structured Exception Handling (SEH) may be employed to wrap native code in SEH try/catch blocks so as to capture machine (CPU/FPU) generated software interrupts (such as NULL pointer access violations and divide-by-zero operations) and to handle these situations before the interrupt is propagated back up into the JVM (i.e. Java side code) and in all likelihood resulting in an unhandled exception;
[edit] How the JNI works
In the JNI framework, native functions are implemented in separate .c or .cpp files. (C++ provides a slightly cleaner interface with JNI.) When the JVM invokes the function, it passes a JNIEnv
pointer, a jobject
pointer, and any Java arguments declared by the Java method. A JNI function may look like this:
JNIEXPORT void JNICALL Java_ClassName_MethodName (JNIEnv *env, jobject obj) { /*Implement Native Method Here*/ }
The env
pointer is a structure that contains the interface to the JVM. It includes all of the functions necessary to interact with the JVM and to work with Java objects. Example JNI functions are converting native arrays to/from Java arrays, converting native strings to/from Java strings, instantiating objects, throwing exceptions, etc. Basically, anything that Java code can do can be done using JNIEnv
, albeit with considerably less ease.
For example, the following converts a Java string to a native string:
//C++ code JNIEXPORT void JNICALL Java_ClassName_MethodName (JNIEnv *env, jobject obj, jstring javaString) { //Get the native string from javaString const char *nativeString = env->GetStringUTFChars(javaString, 0); //Do something with the nativeString //DON'T FORGET THIS LINE!!! env->ReleaseStringUTFChars(javaString, nativeString); }
/*C code*/ JNIEXPORT void JNICALL Java_ClassName_MethodName (JNIEnv *env, jobject obj, jstring javaString) { /*Get the native string from javaString*/ const char *nativeString = (*env)->GetStringUTFChars(env, javaString, 0); /*Do something with the nativeString*/ /*DON'T FORGET THIS LINE!!!*/ (*env)->ReleaseStringUTFChars(env, javaString, nativeString); }
Note that C++ JNI code is syntactically slightly cleaner than C JNI code because like Java, C++ uses object method invocation semantics. That means that in C, the env
parameter is dereferenced using (*env)->
and env
has to be explicitly passed to JNIEnv
methods. In C++, the env
parameter is dereferenced using env->
and the env
parameter is implicitly passed as part of the object method invocation semantics.
Native data types can be mapped to/from Java data types. For compound types such as objects, arrays and strings the native code must explicitly convert the data by calling methods in the JNIEnv
.
[edit] Mapping types
The following table shows the mapping of types between Java and native code.
Native Type | Java Language Type | Description | Type signature |
---|---|---|---|
unsigned char | jboolean | unsigned 8 bits | Z |
signed char | jbyte | signed 8 bits | B |
unsigned short | jchar | unsigned 16 bits | C |
short | jshort | signed 16 bits | S |
int | jint | signed 32 bits | I |
long long |
jlong | signed 64 bits | J |
float | jfloat | 32 bits | F |
double | jdouble | 64 bits | D |
In addition, the signature "L fully-qualified-class ;" would mean the class uniquely specified by that name; e.g., the signature "Ljava/lang/String;" refers to the class java.lang.String. Also, prefixing [
to the signature makes the array of that type; for example, [I
means the int array type.
Here, these types are interchangeable. You can use jint
where you normally use an int
, and vice-versa, without any typecasting required.
However, mapping between Java Strings and arrays to native strings and arrays is different. If you use a jstring
in where a char *
would be, your code could crash the JVM.
// !!! Incorrect !!! // JNIEXPORT void JNICALL Java_ClassName_MethodName (JNIEnv *env, jobject obj, jstring javaString) { printf("%s", javaString); }
// Correct // JNIEXPORT void JNICALL Java_ClassName_MethodName (JNIEnv *env, jobject obj, jstring javaString) { //Get the native string from javaString const char *nativeString = env->GetStringUTFChars(javaString, 0); printf("%s", nativeString); //DON'T FORGET THIS LINE!!! env->ReleaseStringUTFChars(javaString, nativeString); }
This is similar with Java arrays, as illustrated in the example below that takes the sum of all the elements in an array.
// !!! Incorrect !!! // JNIEXPORT jint JNICALL Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr) { int i, sum = 0; for (i = 0; i < 10; i++) { sum += arr[i]; } return sum; }
// Correct // JNIEXPORT jint JNICALL Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr) { jint buf[10]; jint i, sum = 0; env->GetIntArrayRegion(arr, 0, 10, buf); for (i = 0; i < 10; i++) { sum += buf[i]; } return sum; }
Of course, there is much more to it than this. Look for links below for more information.
[edit] JNIEnv*
In each native call JNIEnv argument is only valid during the call. To use the argument outside the call you need to use AttachCurrentThread and DetachCurrentThread, like so:
JNIEnv *env; (*g_vm)->AttachCurrentThread (g_vm, (void **) &env, NULL); // do stuff (*g_vm)->DetachCurrentThread (g_vm);
[edit] Advanced uses
[edit] Native AWT painting
Not only can native code interface with Java, it can also draw on a Java Canvas
, which is possible with the Java AWT Native Interface. The process is almost the same, with just a few changes. The Java AWT Native Interface is only available since J2SE 1.3.
[edit] Access to assembly code
JNI also allows direct access to assembly code, without even going through a C bridge[3]. Accessing Java applications from assembly is also possible in the same way[4].
[edit] Microsoft's RNI
Microsoft's proprietary implementation of a Java Virtual Machine (Visual J++) had a similar mechanism for calling native Windows code from Java, called the Raw Native Interface (RNI). However, following the Sun - Microsoft litigation about this implementation, Visual J++ is no longer maintained.
[edit] See also
- Java AWT Native Interface
- Gluegen A Java tool which automatically generates the Java and JNI code necessary to call C libraries from Java code.
- P/Invoke, the .NET Framework equivalent of JNI.
- SWIG is a multilanguage interface generator for C and C++ libraries that can generate JNI code
- Java Native Access provides Java programs easy access to native shared libraries without writing boiler plate code
[edit] References
- ^ "Role of the JNI". The Java Native Interface Programmer's Guide and Specification. http://java.sun.com/docs/books/jni/html/intro.html#1811. Retrieved on 2008-02-27.
- ^ If JNI based application is crashing, check signal handling!
- ^ "Invoking Assembly Language Programs from Java". Java.net. 2006-10-19. http://today.java.net/pub/a/today/2006/10/19/invoking-assembly-language-from-java.html. Retrieved on 2007-10-06.
- ^ "Launch Java Applications from Assembly Language Programs". Java.net. 2006-10-19. http://today.java.net/pub/a/today/2007/10/04/launch-java-applications-from-assembly-language-programs.html. Retrieved on 2007-10-04.
[edit] External links
- JNI-Java Native Interface
- Java Native Interface: Programmer's Guide and Specification
- Sun's JNI page, including the JNI Specification
- GNU CNI Tutorial
- A JNI Tutorial at CodeProject.com (Microsoft specific)
- JNI Tutorial at CodeToad.com
- Larger JNI example from Sun
- JNI in XCode from Apple
- Exception handling in JNI
- JNIWrapper provides simplified access to native code from Java applications without using Java Native Interface.
- Java to Native Interface LGPL library to call native functions from Java
- Java Native Access Access to native libraries from Java without JNI
- NLink Another library for access to native libraries without JNI
- NativeCall – call native methods from Java without JNI Library to access native code without JNI
- J/Invoke Annotation based pure Java access to Win32 API or Linux/Mac OS X shared libraries, similar to P/Invoke
- JNIEasy Transparent Native Programming for C/C++, pure Java alternative to JNI using POJOS and JDO/JPA development style