Friday, May 29, 2015

JNI (Java Native Interface)

The Java Native Interface (JNI) is a Java layer that allows Java code running in the Java Virtual Machine (JVM) to call and be called by native applications and libraries written in other languages, such as C, C++, and assembly. Developers use the JNI to write native methods to handle situations when an application cannot be written entirely in Java, such as when the standard Java class library does not support the platform-dependent 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 the JNI to provide functionality to the developer and the user, e.g., I/O file reading and sound capabilities. The inclusion of 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 the JNI, developers should make sure the functionality is not already provided in the standard libraries. In this introductory article, I will cover how the JNI works and how native types are mapped to Java types and classes.

How the JNI works

In the JNI, native functions are implemented in a separate .c or .cpp file. (C++ provides a slightly cleaner interface with the 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, jobjectobj)

{
    //Method native implemenation

}
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 and from Java arrays, converting native strings to and from Java strings, instantiating objects, throwing exceptions, etc. Basically, you can use JNIEnv to do anything that Java does, albeit with considerably less ease.
To be more formal, native code accesses JVM features by calling JNI functions, which are available through an interfacepointer (this is a pointer to a pointer). This pointer points to an array of pointers, each of which points to an interface function. Every interface function is at a predefined offset inside the array. Native methods receive the JNI interface pointer as an argument. The JVM is guaranteed to pass the same interface pointer to a native method when it makes multiple calls to the native method from the same Java thread. However, a native method can be called from different Java threads, and therefore may receive different JNI interface pointers.
Native methods are loaded with the System.loadLibrary method. In the following example, the class initialization method loads a platform-specific native library in which the native method f is defined:
packagepkg;  

class Cls { 
     native double f(inti, String s); 
     static { 
         System.loadLibrary(pkg_Cls"); 
     } 

}
The argument to System.loadLibrary is a library name chosen arbitrarily by the programmer. The system follows a standard, platform-specific approach to convert the library name to a native library name. For example, a Solaris system converts the name pkg_Cls to libpkg_Cls.so, while a Win32 system converts the same pkg_Cls name to pkg_Cls.dll.
Dynamic linkers resolve entries based on their names. A native method name is concatenated from components, which include: the prefix Java_, a mangled fully-qualified class name, and a mangled method name.
Note: Microsoft's implementation of a JVM has a similar mechanism for calling native Windows code from Java called the Raw Native Interface (RNI).

* Dynamic Linker : In computing, a dynamic linker is the part of an operating system that loads and links the shared libraries needed by an executable when it is executed (at "run time"), by copying the content of libraries from persistent storage to RAM, and filling jump tables and relocating pointers. The specific operating system and executable format determine how the dynamic linker functions and how it is implemented.
Linking is often referred to as a process that is performed when the executable is compiled, while a dynamic linker is a special part of an operating system that loads external shared libraries into a running process and then binds those shared libraries dynamically to the running process. This approach is also called dynamic linking or late linking.

* Jump Table : In computer programming, a branch table or jump table is a method of transferring program control (branching) to another part of a program (or a different program that may have been dynamically loaded) using a table of branch or jump instructions.

Data type mapping

Primitive types, such as integers, characters, and so on, are copied between Java and native code. Arbitrary Java objects, on the other hand, are passed by reference.
This table shows the mapping of types between Java and native code. 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 where a char * would be, your code could crash the JVM. This is an example of how you should work correctly with strings:
JNIEXPORT void JNICALL Java_ClassName_MethodName
  (JNIEnv *env, jobjectobj, jstringjavaString)

{
    //Get the native string from Java string
    const char *nativeString = env->GetStringUTFChars(env,javaString, 0);
    printf("%s", nativeString);
    env->ReleaseStringUTFChars(env,javaString, nativeString);

}
You always manipulate Java objects using the interface pointer env.

No comments:

Post a Comment