Java本地接口

JNI （Java Native Interface，Java本地接口）是一种编程框架，使得Java虚拟机中的Java程序可以调用本地应用/或库，也可以被其他程序调用。 本地程序一般是用其它语言（C、C++或汇编语言等）编写的，并且被编译为基于本机硬件和操作系统的程序。
设计目的和功能.
有些事情Java无法处理时，JNI允许程序员用其他编程语言来解决，例如，Java标准库不支持的平台相关功能或者程序库。也用于改造已存在的用其它语言写的程序，供Java程序调用。许多基于JNI的标准库提供了很多功能给程序员使用，例如文件I/O、音频相关的功能。当然，也有各种高性能的程序，以及平台相关的API实现，允许所有Java应用程序安全并且平台独立地使用这些功能。 
JNI框架允许Native方法调用Java对象，就像Java程序访问Native对象一样方便。Native方法可以创建Java对象，读取这些对象，并调用Java对象执行某些方法。当然Native方法也可以读取由Java程序自身创建的对象，并调用这些对象的方法。
JNI如何工作.
在JNI框架，native方法一般在单独的.c或.cpp文件中实现。当JVM调用这些函数，就传递一个codice_1指针，一个codice_2的指针，任何在Java方法中声明的Java参数。一个JNI函数看起来类似这样： 
JNIEXPORT void JNICALL Java_ClassName_MethodName
 (JNIEnv *env, jobject obj)
 /*Implement Native Method Here*/
"codice_3"指向一个结构包含了到JVM的界面，包含了所有必须的函数与JVM交互、访问Java对象。例如，把本地数组转换为Java数组的JNI函数，把本地字符串转换为Java字符串的JNI函数，实例化对象，抛出异常等。基本上，Java程序可以做的任何事情都可以用codice_1做到，虽然相当不容易。 
例如，下面代码把Java字符串转化为本地字符串：
//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-&gt;GetStringUTFChars(javaString, 0);
 //Do something with the nativeString
 //DON'T FORGET THIS LINE!!!
 env-&gt;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)-&gt;GetStringUTFChars(env, javaString, 0);
 /*Do something with the nativeString*/
 /*DON'T FORGET THIS LINE!!!*/
 (*env)-&gt;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);
本地数据类型与Java数据类型可以互相映射。对于复合数据类型，如对象，数组，字符串，就必须用codice_1中的方法来显示地转换。
第2个参数"obj"引用到一个Java对象，在其中声明了本地方法。 
类型映射.
下表是Java (JNI)与本地代码之间的数据类型映射：
签名codice_6是由该名字指明的类。例如，签名codice_7是类codice_8。带前缀codice_9的签名表示该类型的数组，如codice_10表示整型数组。codice_11签名使用codice_12代码。
这些类型是可以互换的，如codice_13也可使用 codice_14，不需任何类型转换。
但是，Java字符串、数组与本地字符串、数组是不同的。如果在使用codice_15代替了codice_16，程序可能会导致JVM崩溃。
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)-&gt;GetStringUTFChars(env, javaString, 0);
 printf("%s", nativeString);
 (*env)-&gt;ReleaseStringUTFChars(env, javaString, nativeString);
这种情况也适用于Java数组。下例对数组元素求和。
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-&gt;GetIntArrayRegion(arr, 0, 10, buf);
 for (i = 0; i &lt; 10; i++) {
 sum += buf[i];
 return sum;
JNIEnv*.
JNI环境指针(JNIEnv*)作为每个映射为Java方法的本地函数的第一个参数，使得本地函数可以与JNI环境交互。这个JNI界面指针可以存储，但仅在当前线程中有效。其它线程必须首先调用AttachCurrentThread()把自身附加到虚拟机以获得JNI界面指针。一旦附加，本地线程运行就类似执行本地函数的正常Java线程。本地线程直到执行DetachCurrentThread()把自身脱离虚拟机。
把当前线程附加到虚拟机并获取JNI界面指针：
 JNIEnv *env;
 (*g_vm)-&gt;AttachCurrentThread (g_vm, (void **) &amp;env, NULL);
当前线程脱离虚拟机：
 (*g_vm)-&gt;DetachCurrentThread (g_vm);
高级使用.
本地AWT绘制.
本地代码不仅可以与Java交互，也可以在Java 绘图，使用。 
访问汇编代码.
JNI允许直接访问汇编代码。 也可以从汇编代码访问Java。
Microsoft的RNI.
Microsoft实现的Java虚拟机——Visual J++的类似的访问本地Windows代码的机制Raw Native Interface（RNI）。 
例子.
HelloWorld.
make.sh
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
javac HelloWorld.java
javah HelloWorld
gcc -shared libHelloWorld.c -o libHelloWorld.so
java HelloWorld
build.bat
SET VC="C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC"
SET MSDK="C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A"
SET JAVA_HOME="C:\Program Files (x86)\Java\jdk1.7.0_21"
call %VC%\vcvarsall.bat
javac HelloWorld.java
javah HelloWorld
%VC%\bin\cl /I%JAVA_HOME%\include /I%JAVA_HOME%\include\win32 /I%VC%\include /I%VC%\lib /I%MSDK%\Lib libHelloWorld.c /FelibHelloWorld.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 */
/* Header for class HelloWorld */
extern "C" {
 * Class: HelloWorld
 * Method: print
 * Signature: ()V
JNIEXPORT void JNICALL Java_HelloWorld_print
 (JNIEnv *, jobject);
libHelloWorld.c
 #include &lt;stdio.h&gt;
 #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