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 的過程中，可能因為某些微小的 BUG，對整個 JVM 造成很難重現和除錯的錯誤。
* 僅有應用程式與簽章的 applet 可以呼叫 JNI。
* 依賴於 JNI 的應用失去了 Java 的平台移植性（一種解決辦法是為每個平台編寫專門的 JNI 代碼，然後在 Java 代碼中，根據作業系統載入正確的 JNI 代碼）。
* JNI 框架並沒有對 non-JVM 記憶體提供自動垃圾回收機制，Native 代碼（如組合語言）分配的記憶體和資源，需要其自身負責進行顯式的釋放。
* Linux 與 Solaris 平台，如果 Native 代碼將自身註冊為訊號處理器（signal handler），就會攔截發給 JVM 的訊號。可以使用 責任鏈模式 讓 Native 代碼更好地與 JVM 進行互動。
* Windows 平台上，在 SEH try/catch 塊中可以將結構化例外處理（SEH）用來包裝 Native 代碼，以擷取機器（CPU/FPU）生成的軟中斷（例如：空指標異常、被除數為 0 等），將這些中斷在傳播到 JVM（中的 Java 代碼）之前進行處理，以免造成未擷取的異常。
* NewStringUTF、GetStringUTFLength、GetStringUTFChars、ReleaseStringUTFChars 與 GetStringUTFRegion 等編碼函式處理的是一種修改的 UTF-8，，實際上是一種不同的編碼，某些字元並不是標準的 UTF-8。 null 字元（U+0000）以及不在 Unicode 字元平面對映中的字元（codepoints 大於等於 U+10000 的字元，例如 UTF-16 中的代理對 surrogate pairs），在修改的 UTF-8 中的編碼都有所不同。 許多程式錯誤地使用了這些函式，將標準 UTF-8 字串傳入或傳出這些函式，實際上應該使用修改後的編碼。程式應當先使用 NewString、GetStringLength、GetStringChars、ReleaseStringChars、GetStringRegion、GetStringCritical 與 ReleaseStringCritical 等函式，這些函式在小尾序機器上使用 UTF-16LE 編碼，在大尾序機器上使用 UTF-16BE 編碼，然後再通過程式將 UTF-16 轉換為 UTF-8。
* JNI 在某些情況下可能帶來很大的開銷和效能損失：
  * 呼叫 JNI 方法是很笨重的操作，特別是在多次重複呼叫的情況下。
  * Native 方法不會被 JVM 行內，也不會被 即時編譯 最佳化 ，因為方法已經被編譯過了。
  * Java 陣列可能會被拷貝一份，以傳遞給 native 方法，執行完之後再拷貝回去。其開銷與陣列的長度是線性相關的。
  * 如果傳遞一個物件給方法，或者需要一個回呼，那麼 Native 方法可能會自己呼叫 JVM。 存取 Java 物件的屬性、方法和類型時，Native 代碼需要類似反射的東西。簽章由字串指定，通常從 JVM 中查詢。這非常緩慢並且容易出錯。
  * Java 中的字串（String） 也是物件，有 length 屬性，並且是編碼過的。讀取或者建立字串都需要一次時間複雜度為 O (n) 的複製操作.

## JNI 如何工作

在 JNI 框架，native 方法一般在單獨的.c 或.cpp 檔案中實現。當 JVM 呼叫這些函式，就傳遞一個 `JNIEnv` 指標，一個 `jobject` 的指標，任何在 Java 方法中聲明的 Java 參數。一個 JNI 函式看起來類似這樣：

_`env`_ 指向一個結構包含了到 JVM 的介面，包含了所有必須的函式與 JVM 互動、存取 Java 物件。例如，把本地陣列轉換為 Java 陣列的 JNI 函式，把本地字串轉換為 Java 字串的 JNI 函式，實例化物件，丟擲異常等。基本上，Java 程式可以做的任何事情都可以用 `JNIEnv` 做到，雖然相當不容易。

例如，下面代碼把 Java 字串轉化為本地字串：

本地資料類型與 Java 資料類型可以互相對映。對於複合資料類型，如物件，陣列，字串，就必須用 `JNIEnv` 中的方法來顯示地轉換。

第 2 個參數 _obj_ 參照到一個 Java 物件，在其中聲明了本地方法。

### 類型對映

下表是 Java (JNI) 與本地代碼之間的資料類型對映：

簽章 `"L fully-qualified-class ;"` 是由該名字指明的類。例如，簽章 `"Ljava/lang/String;"` 是類 `java.lang.String`。帶字首 `[` 的簽章表示該類型的陣列，如 `[I` 表示整型陣列。`void` 簽章使用 `V` 代碼。

這些類型是可以互換的，如 `jint` 也可使用 `int`，不需任何類型轉換。

但是，Java 字串、陣列與本地字串、陣列是不同的。如果在使用 `char *` 代替了 `jstring`，程式可能會導致 JVM 崩潰。

這種情況也適用於 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

build.bat

HelloWorld.java

HelloWorld.h

libHelloWorld.c

Invocation:

## 參見

* Java AWT Native Interface
* Gluegen, a Java tool which automatically generates the Java and JNI code necessary to call C libraries from Java code
* P/Invoke, the .NET Framework method of calling native applications
* SWIG, a multilanguage interface-generator for C and C++ libraries that can generate JNI code
* Java Native Access provides Java programs easy access to native shared libraries without writing boilerplate code

## 參考文獻

## 相關書籍

## 外部連結

* Oracle's JNI page for Java 6, including the JNI Specification （頁面存檔備份，存於網際網路檔案館）
* Java Native Interface: Programmer's Guide and Specification（頁面存檔備份，存於網際網路檔案館） - Book, copyright 2002.
* Best practices for using the Java Native Interface （頁面存檔備份，存於網際網路檔案館）
* JNI Complete tutorial with examples
* GNU CNI Tutorial
* Multi-platform JNI Tutorial at Think-Techie.com
* A JNI Tutorial at CodeProject.com (Microsoft specific)
* JNI Tutorial at CodeToad.com （頁面存檔備份，存於網際網路檔案館）
* Larger JNI example from Sun （頁面存檔備份，存於網際網路檔案館）
* JNI video tutorial with Eclipse and Visual Studio（頁面存檔備份，存於網際網路檔案館）
* JNI in XCode from Apple（頁面存檔備份，存於網際網路檔案館）
* Exception handling in JNI （頁面存檔備份，存於網際網路檔案館）
* HawtJNI Simplifies creating JNI libraries by code generating the JNI implementations using declarative annotations placed on your Java code.
* J/Link （頁面存檔備份，存於網際網路檔案館） lets you call Java from Mathematica in a completely transparent way, and it also lets you use and control the Mathematica kernel from a Java program (Commercial)
* Jace （頁面存檔備份，存於網際網路檔案館） is a toolkit designed to make it easy to write JNI-based programs
* JNIWrapper （頁面存檔備份，存於網際網路檔案館） provides simplified access to native code from Java applications without using Java Native Interface.
* Java to Native Interface （頁面存檔備份，存於網際網路檔案館） LGPL library to call native functions from Java
* 永久失效連結] Java Native Access Access to native libraries from Java without JNI
* NLink Another library for access to native libraries without JNI
* NativeCall – call native methods from Java without JNI Library to access native code without JNI
* JNIEasy （頁面存檔備份，存於網際網路檔案館） Transparent Native Programming for C/C++, pure Java alternative to JNI using POJOS and JDO/JPA development style
* jni4net（頁面存檔備份，存於網際網路檔案館） bridge between Java and .NET (intraprocess, fast, object oriented, open-source)
* Object-Oriented JNI Advanced Add-in for VC6 Object-Oriented JNI with a number of helpers that includes the standard JNI SDK for regular C++ (Commercial)
* Object-Oriented JNI for .NET1.1 (low-level) Object-Oriented JNI with a number of helpers that includes the standard JNI SDK for C#, Managed C++, VB#, J# (Commercial)
* Object-Oriented JNI for .NET2.0 (low-level) Object-Oriented JNI with a number of helpers that includes the standard JNI SDK for C#, Managed C++, VB#, J# (Commercial)
* OOJNI Add-in (C#,VB#) for VS2005/2008 Generates object-oriented JNI code in C# or VB# for Java classes selected, implements Java interfaces and Java native methods in VB# and C#. Java Class methods and fields (which are represented as .NET Class properties) to be wrapped can be filtered. (Commercial)
* _e_X_treme_DB JNI（頁面存檔備份，存於網際網路檔案館） uses Java annotations and reflection to enable Java applications to call the EXtremeDB database (written in C) without reliance on an external database definition language
* JavaToDPR （頁面存檔備份，存於網際網路檔案館）, a Delphi Project (.dpr) Stub File Generator that allows one to write an Embarcadero Delphi DLL to handle the native methods declared in a Java .class file