Educational goals
Introduction
Glossary
JNI overview
Example
Exercises
References
The Java Native Interface, JNI, is just that; an interface. It's an API that allows Java code to interact with code written in another language, typically C or C++. Because the JNI is an interface, JVM vendors are free to implement the virtual machine as they see fit. As long as the JVM follows the specification of the JNI, all native code written to the specification should work with that JVM.Each software project has its own requirements and constraints, so the decision to use native code with Java is highly dependent on the particular situation. Although there may be several reasons that one would want to use native code, two common justifications are code reuse and performance.
Code Reusability. It may be argued that (superficially) Java is a better C++ (maybe in the way that C++ is a better C). However, the benefits of using Java at this level do not outweigh the overwhelming task of re-implementing currently tested and debugged C++ code. Given large amounts of proven C++ code, it may be more cost-effective to incorporate its functionality into a Java program, rather than to port (re-implement/test/debug) the C++ functionality in Java.
Performance. Given that Java is interpreted (more or less), one would assume that compiled native code would perform much better. It may be useful to "hand-code" native methods where speed is critical. However, the rise in JIT compilers and other performance-enhancing technologies may render complaints of Java's poor performance invalid. Only time will tell.
Please follow the link to the glossary page.
The Java Development Kit (JDK) provides tools and library routines that help the Java programmer interface with native code. Microsoft and Sun both provide their own implementations of the JDK, and are incompatible with each other. All of the examples and discussions use Sun's implementation. Later in this lesson, we will explore the differences in Microsoft's implementation and understand the pros and cons of their approach. Incidentally, Microsoft refers to their implementation as the Raw Native Interface, RNI.javah
javah is a useful tool that creates a C-style header file from a given class. The resulting header file describes the class file in C terms. Although it is possible to manually create the header file, this is almost always a bad idea. javah knows exactly how Java types and objects map into C types. For example, an int in Java maps to a long in C, and a long in Java maps to a 64-bit value, _int64, in native code.
jni.h
jni.h is a C/C++ header file that is included with the JDK. This file defines all of the necessary data types. It contains mostly #defines and typedefs that hide the complexity of mapping Java types to native types. It also #includes many other platform-specific header files. Since javah automatically includes this header file, you as the programmer usually do not have to explicity include this file.
It is important that the data types that are passed between Java and native code have the same properties. The easiest and safest way is to use the table below when mapping between Java and native code. As you can see, with the exception of void, the native types simply have a j prepended to the Java type, which makes it easy to remember.
Java Type Native Type Description boolean jboolean 8 bits, unsigned byte jbyte 8 bits, signed char jchar 16 bits, unsigned double jdouble 64 bits float jfloat 32 bits int jint 32 bits, signed long jlong 64 bits, signed short jshort 16 bits, signed void void N/A
JNI type signatures
In native code, Java type signatures are encoded using the mappings shown below. The exact use of these mappings will be shown in Exercise #2.
Java Type Signature boolean Z byte B char C double D float F int I long J void V object Lfully-qualified-class; type[] [type method signature ( arg-types) ret-type Examples:
Method Signature void f1() ()V int f2(int, long) (IJ)I boolean f3(int[]) ([I)B double f4(String, int) (Ljava/lang/String;I)D void f5(int, String [], char) (I[Ljava/lang/String;C)V
One of the best ways to learn a new programming skill is by example. In
keeping with tradition, we present the canonical programming example that
simply prints the words "Hello World!" to the display. The twist is that
Java code will invoke a native function to do the actual printing via the C
library function, printf. A more detailed explanation of this example
can be found here.
1. Create the Java files. First, create the two Java files as shown below.
HelloWorld.java2. Compile the Java files.class HelloWorld { public native void displayMessage(); static { System.loadLibrary("HelloWorldImp"); } }Main.javaclass Main { public static void main(String[] args) { HelloWorld hello = new HelloWorld(); hello.displayMessage(); } }3. Create the header file. Uses the .class file created previously to create HelloWorld.h. (Note the -jni argument to javah.)javac HelloWorld.java javac Main.java4. Create the C++ file. This example shows the native code with a .cpp extension, but this file could just have easily had a .c extension since there is nothing "C++" about it.javah -jni HelloWorldHelloWorld.cpp5. Build the shared library from native code. This is where some of the differences show up. Make sure that the search paths are correct and that the file extensions are what the compiler expects. Also, the convention in Unix is to prepend lib onto the names of library files and to append .so onto the names of shared library files.#include <stdio.h> #include "HelloWorld.h" // this header file was generated by javah JNIEXPORT void JNICALL Java_HelloWorld_displayMessage(JNIEnv *env, jobject obj) { printf("Hello World!\n"); }6. Execute the program. On Unix, you may have to move the shared library into a directory that's in your search path, or add the current directory to your path. Under Windows NT, the current directory is searched by default.# This works on sirius.cs.pdx.edu, note .C extension g++ -G -I/pkgs/jdk1.1.1/include -I/pkgs/jdk1.1.1/include/solaris HelloWorld.C -o libHelloWorldImp.so : This should work under NT. Replace the include paths to match your environment : and be sure to include Sun's JDK not Microsoft's! cl -Im:\jdk1.1.5\include -Im:\jdk1.1.5\include\win32 -LD HelloWorld.cpp -FeHelloWorldImp.dlljava Main Hello World! <--- this is displayed on the screen
Please follow the link to the exercises page.
Websites and online documents:Books:
- JNI - Java Native Interface. This document contains links to a step-by-step tutorial ,details on Java Native Interface Programming, the JNI specification, and a JNI FAQ. The FAQ is pretty thin, but this is the place to start on Sun's page.
- Using Java with Native Code. This is Microsoft's implementation of the JNI. If you're stuck with Windows and can't use Sun's JDK, this is a must have.
Other related work:
- Essential JNI: Java Native Interface (Essential Java) by Rob Gordon, Robert Gordon, Alan McClellan (Prentice Hall Computer Books ISBN 0136798950, April 1998)
- Using the Java Native Interface with C++. This document has a wealth of examples and techniques for using the JNI. There is a detailed comparison between Microsoft's implementation and Sun's implementation, including the differences in syntax and runtime performance. In addition to the basics, there are examples that demonstrate two-dimensional arrays, callbacks, and exceptions, among other concepts.
Copyright © 1998
Sergio Antoy.
All Rights Reserved February 99, version 020199 |