Java Native Interface

"JNI" redirects here. For the city in the province of Buenos Aires, see Junín, Buenos Aires.

In computing, the Java Native Interface (JNI) is a programming framework that enables Java code running in a Java Virtual Machine (JVM) to call and be called by[1] native applications (programs specific to a hardware and operating system platform) and libraries written in other languages such as C, C++ and assembly.

Purpose and features

JNI enables programmers 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 applicationwritten in another programming languageto 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. file I/O 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.

The JNI framework lets a native method use 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.

Pitfalls

How the JNI works

In the JNI framework, native functions are implemented in separate .c or .cpp files. (C++ provides a slightly simpler 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.

The argument obj is a reference to the Java object inside which this native method has been declared.

For example, the following converts a Java string to a native string:

//C++ code
extern "C"
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);
}
/*Objective-C code*/
JNIEXPORT void JNICALL Java_ClassName_MethodName
  (JNIEnv *env, jobject obj, jstring javaString)
{
    /*DON'T FORGET THIS LINE!!!*/
    JNF_COCOA_ENTER(env);

    /*Get the native string from javaString*/
    NSString* nativeString = JNFJavaToNSString(env, javaString);

    /*Do something with the nativeString*/

    /*DON'T FORGET THIS LINE!!!*/
    JNF_COCOA_EXIT(env);
}

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.

Mapping types

The following table shows the mapping of types between Java (JNI) 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
long jint signed 32 bits I

long long
__int64

jlong signed 64 bits J
float jfloat 32 bits F
double jdouble 64 bits D
void V

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. Finally, a void signature uses the V code.

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.

/*C code*/
JNIEXPORT void JNICALL Java_ClassName_MethodName
        (JNIEnv *env, jobject obj, jstring javaString) {
    // printf("%s", javaString);        // INCORRECT: Could crash VM!

    // Correct way: Create and release native string from Java string
    const char *nativeString = (*env)->GetStringUTFChars(env, javaString, 0);
    printf("%s", nativeString);
    (*env)->ReleaseStringUTFChars(env, 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.

JNIEXPORT jint JNICALL Java_IntArray_sumArray
        (JNIEnv *env, jobject obj, jintArray arr) {
    jint buf[10];
    jint i, sum = 0;
    // This line is necessary, since Java arrays are not guaranteed
    // to have a continuous memory layout like C arrays.
    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.

JNIEnv*

A JNI environment pointer (JNIEnv*) is passed as an argument for each native function mapped to a Java method, allowing for interaction with the JNI environment within the native method. This JNI interface pointer can be stored, but remains valid only in the current thread. Other threads must first call AttachCurrentThread() to attach themselves to the VM and obtain a JNI interface pointer. Once attached, a native thread works like a regular Java thread running within a native method. The native thread remains attached to the VM until it calls DetachCurrentThread() to detach itself.[4]

To attach to the current thread and get a JNI interface pointer:

 JNIEnv *env;
 (*g_vm)->AttachCurrentThread (g_vm, (void **) &env, NULL);

To detach from the current thread:

 (*g_vm)->DetachCurrentThread (g_vm);

Advanced uses

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.

Access to assembly code

JNI also allows direct access to assembly code, without even going through a C bridge.[5] Accessing Java applications from assembly is also possible in the same way.[6]

Microsoft's J/Direct and RNI

Microsoft's discontinued proprietary implementation of a Java Virtual Machine (Visual J++) has a similar mechanism for calling native code from Java, called the Raw Native Interface (RNI). In addition, it has an easy way to call existing native code that isn't itself aware of Java, such as (but not limited to) the Windows API, called J/Direct. However, following the Sun - Microsoft litigation about this implementation, Visual J++ is no longer maintained.

RNI was less clumsy to use than JNI, because no bookkeeping with a Java environment pointer was needed. Instead, all Java objects could be accessed directly. To facilitate this, a tool was used that generated header files from Java classes. Similarly, J/Direct was easier to use than using the necessary intermediate native library and JNI, although at present JNA is an alternative.

Examples

HelloWorld

make.sh

#!/bin/sh

# Linux 2.6.32-358.el6.x86_64
# gcc 4.4.7
# openjdk 1.7.0

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
javac HelloWorld.java
javah HelloWorld
gcc -shared -fPIC libHelloWorld.c -o libHelloWorld.so
java HelloWorld

build.bat

:: Microsoft Visual Studio 2012 Visual C++ compiler
SET VC=C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC
:: Microsoft Windows SDK for Windows 7 and .NET Framework 4 
SET MSDK=C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A
:: Java 1.7.0 update 21
SET JAVA_HOME=C:\Program Files (x86)\Java\jdk1.7.0_21
:: Add compiler tools folder to the PATH variable.  Do not run this too many times or the PATH will exceed the maximum limit.
call "%VC%\vcvarsall.bat"

javac HelloWorld.java
javah HelloWorld
:: On Windows, the JNI library should not have a "lib" prefix
%VC%\bin\cl "/I%JAVA_HOME%\include" "/I%JAVA_HOME%\include\win32" "/I%VC%\include" "/I%VC%\lib" "/I%MSDK%\Lib" "libHelloWorld.c" "/FeHelloWorld.dll" /LD
java HelloWorld

HelloWorld.java

class HelloWorld
{
    private native void print();
    public static void main(String[] args)
    {
        new HelloWorld().print();
    }
    static {
        System.loadLibrary("HelloWorld");
    }
}

HelloWorld.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */

#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloWorld
 * Method:    print
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_HelloWorld_print
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

libHelloWorld.c

 #include <stdio.h>
 #include "HelloWorld.h"

 JNIEXPORT void JNICALL
 Java_HelloWorld_print(JNIEnv *env, jobject obj)
 {
     printf("Hello World!\n");
     return;
 }

Invocation:

$ chmod +x make.sh
$ ./make.sh

See also

References

  1. "Role of the JNI". The Java Native Interface Programmer's Guide and Specification. Retrieved 2008-02-27.
  2. Modified UTF-8 Strings
  3. http://stackoverflow.com/a/7809300/1068537
  4. The Invocation API. Sun Microsystems. http://java.sun.com/j2se/1.5.0/docs/guide/jni/spec/invocation.html
  5. "Invoking Assembly Language Programs from Java". Java.net. 2006-10-19. Retrieved 2007-10-06.
  6. "Launch Java Applications from Assembly Language Programs". Java.net. 2006-10-19. Retrieved 2007-10-04.

Bibliography

External links

This article is issued from Wikipedia - version of the Tuesday, January 26, 2016. The text is available under the Creative Commons Attribution/Share Alike but additional terms may apply for the media files.