Java本地接口

設計目的和功能

JNI允許程式設計師用其他程式語言來解決用純粹的Java程式碼不好處理的情況，例如，Java標準庫不支援的平台相關功能或者程式庫。也用於改造已存在的用其它語言寫的程式，供Java程式呼叫。許多基於JNI的標準庫提供了很多功能給程式設計師使用，例如檔案I/O、音訊相關的功能。當然，也有各種高效能的程式，以及平台相關的API實現，允許所有Java應用程式安全並且平台獨立地使用這些功能。 
JNI框架允許Native方法呼叫Java物件，就像Java程式存取Native物件一樣方便。Native方法可以建立Java物件，讀取這些物件，並呼叫Java物件執行某些方法。當然Native方法也可以讀取由Java程式自身建立的物件，並呼叫這些物件的方法。

JNI如何工作

在JNI框架，native方法一般在單獨的.c或.cpp檔案中實現。當JVM呼叫這些函式，就傳遞一個JNIEnv指標，一個jobject的指標，任何在Java方法中聲明的Java參數。一個JNI函式看起來類似這樣： 
JNIEXPORT void JNICALL Java_ClassName_MethodName
  (JNIEnv *env, jobject obj){
    /*Implement Native Method Here*/}
env指向一個結構包含了到JVM的介面，包含了所有必須的函式與JVM互動、存取Java物件。例如，把本地陣列轉換為Java陣列的JNI函式，把本地字串轉換為Java字串的JNI函式，實體化物件，丟擲異常等。基本上，Java程式可以做的任何事情都可以用JNIEnv做到，雖然相當不容易。 
例如，下面程式碼把Java字串轉化為本地字串：
//C++ codeextern "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);}
本地資料類型與Java資料類型可以互相對映。對於複合資料類型，如物件，陣列，字串，就必須用JNIEnv中的方法來顯示地轉換。
第2個參數obj參照到一個Java物件，在其中聲明了本地方法。

JNIEnv*

JNI環境指標(JNIEnv*)作為每個對映為Java方法的本地函式的第一個參數，使得本地函式可以與JNI環境互動。這個JNI介面指標可以儲存，但僅在目前執行緒中有效。其它執行緒必須首先呼叫AttachCurrentThread()把自身附加到虛擬機器以獲得JNI介面指標。一旦附加，本地執行緒執行就類似執行本地函式的正常Java執行緒。本地執行緒直到執行DetachCurrentThread()把自身脫離虛擬機器。
把目前執行緒附加到虛擬機器並取得JNI介面指標：
JNIEnv *env;
(*g_vm)->AttachCurrentThread (g_vm, (void **) &env, NULL);
目前執行緒脫離虛擬機器：
(*g_vm)->DetachCurrentThread (g_vm);

本地AWT繪製

本地程式碼不僅可以與Java互動，也可以在Java Canvas繪圖，使用Java AWT Native Interface。

存取組譯程式碼

JNI允許直接存取組譯程式碼。 也可以從組譯程式碼存取Java。

Microsoft的RNI

Microsoft實現的Java虛擬機器——Visual J++的類似的存取本地Windows程式碼的機制Raw Native Interface（RNI）。

HelloWorld

make.sh
#!/bin/sh# openbsd 4.9# gcc 4.2.1# openjdk 1.7.0export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
javac HelloWorld.java
javah HelloWorld
gcc -shared libHelloWorld.c -o libHelloWorld.so
java HelloWorld
build.bat
:: Microsoft Visual Studio 2012 Visual C++ compilerSET 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 21SET 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 */#include <jni.h>/* Header for class HelloWorld */#ifndef _Included_HelloWorld#define _Included_HelloWorld#ifdef __cplusplusextern "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