1. 簡介html
Java 本地接口概述 java
背景程序員
目標安全
Java 本地接口方法數據結構
Get<PrimitiveType>ArrayElements 例程
Release<PrimitiveType>ArrayElements 例程
Get<PrimitiveType>ArrayRegion 例程
Set<PrimitiveType>ArrayRegion 例程
1 - 簡介
本章介紹 Java 本地接口(Java Native Interface,JNI)。JNI 是本地編程接口。它使得在 Java 虛擬機 (VM) 內部運行的 Java 代碼可以與用其它編程語言(如 C、C++ 和彙編語言)編寫的應用程序和庫進行互操做。
JNI 最重要的好處是它沒有對底層 Java 虛擬機的實現施加任何限制。所以,Java 虛擬機廠商能夠在不影響虛擬機其它部分的狀況下添加對 JNI 的支持。程序員只需編寫一種版本的本地應用程序或庫,就可以與全部支持 JNI 的 Java 虛擬機協同工做。
本章論及如下主題:
Java 本地接口概述
儘管能夠徹底用 Java 編寫應用程序,可是有時單獨用 Java 不能知足應用程序的須要。程序員使用 JNI 來編寫 Java 本地方法,能夠處理那些不能徹底用 Java 編寫應用程序的狀況。
如下示例說明了什麼時候須要使用 Java 本地方法:
經過用 JNI 編程,能夠將本地方法用於:
也能夠與調用 API 一塊兒使用 JNI,以容許任意本地應用程序嵌入到 Java 虛擬機中。這樣使得程序員可以輕易地讓已有應用程序支持 Java,而沒必要與虛擬機源代碼相連接。
背景
目前,不一樣廠商的虛擬機提供了不一樣的本地方法接口。這些不一樣的接口使程序員不得不在給定平臺上編寫、維護和分發多種版本的本地方法庫。
下面簡要分析一下部分已有本地方法接口,例如:
JDK 1.0 本地方法接口
JDK 1.0 附帶有本地方法接口。遺憾的是,有兩點緣由使得該接口不適合於其它 Java 虛擬機。
第一,平臺相關代碼將 Java 對象中的域做爲 C 結構的成員來進行訪問。可是,Java 語言規範沒有規定在內存中對象是如何佈局的。若是 Java 虛擬機在內存中佈局對象的方式有所不一樣,程序員就不得不從新編譯本地方法庫。
第二,JDK 1.0 的本地方法接口依賴於保守的垃圾收集器。例如,無限制地使用 unhand 宏使得有必要以保守方式掃描本地堆棧。
Java 運行時接口
Netscape 建議使用 Java 運行時接口 (JRI),它是 Java 虛擬機所提供服務的通用接口。JRI 的設計融入了可移植性---它幾乎沒有對底層 Java 虛擬機的實現細節做任何假設。JRI 提出了各類各樣的問題,包括本地方法、調試、反射、嵌入(調用)等等。
原始本地接口和 Java/COM 接口
Microsoft Java 虛擬機支持兩種本地方法接口。在低一級,它提供了高效的原始本地接口 (RNI)。RNI 提供了與 JDK 本地方法接口有高度源代碼級的向後兼容性,儘管它們之間還有一個主要區別,即平臺相關代碼必須用 RNI 函數來與垃圾收集器進行顯式的交互,而不是依賴於保守的垃圾收集。
在高一級,Microsoft 的 Java/COM 接口爲 Java 虛擬機提供了與語言無關的標準二進制接口。Java 代碼能夠象使用 Java 對象同樣來使用 COM 對象。Java 類也能夠做爲 COM 類顯示給系統的其他部分。
目標
咱們認爲統一的,通過細緻考慮的標準接口可以向每一個用戶提供如下好處:
得到標準本地方法接口的最佳途徑是聯合全部對 Java 虛擬機有興趣的當事方。所以,咱們在 Java 得到許可方之間組織了一系列研討會,對設計統一的本地方法接口進行了討論。從研討會能夠明確地看出標準本地方法接口必須知足如下要求:
Java 本地接口方法
咱們但願採用一種已有的方法做爲標準接口,由於這樣程序員(程序員不得不學習在不一樣虛擬機中的多種接口)的工做負擔最輕。遺憾的是,已有解決方案中沒有任何方案可以徹底地知足咱們的目標。
Netscape 的 JRI 最接近於咱們所設想的可移植本地方法接口,於是咱們採用它做爲設計起點。熟悉 JRI 的讀者將會注意到在 API 命名規則、方法和域 ID 的使用、局部和全局引用的使用,等等中的類似點。雖然咱們進行了最大的努力,可是 JNI 並不具備對 JRI 的二進制兼容性,不過虛擬機既能夠支持 JRI,又能夠支持 JNI。
Microsoft 的 RNI 是對 JDK 1.0 的改進,由於它能夠解決使用非保守的垃圾收集器的本地方法的問題。然而,RNI 不適合用做與虛擬機無關的本地方法接口。與 JDK 相似,RNI 本地方法將 Java 對象做爲 C 結構來訪問。這將致使兩個問題:
做爲二進制標準,COM 確保了不一樣虛擬機之間的徹底二進制兼容性。調用 COM 方法只要求間接調用,而這幾乎不會佔用系統開銷。另外,COM 對象對動態連接庫解決版本問題的方式也有很大的改進。
然而,有幾個因素阻礙了將 COM 用做標準 Java 本地方法接口:
雖然咱們沒有將 Java 對象做爲 COM 對象暴露給平臺相關代碼,可是 JNI 接口自身與 COM 具備二進制兼容性。咱們採用與 COM 同樣的跳轉表和調用約定。這意味着,一旦具備對 COM 的跨平臺支持,JNI 就能成爲 Java 虛擬機的 COM 接口。
咱們認爲 JNI 不該該是給定 Java 虛擬機所支持的惟一的本地方法接口。標準接口的好處在於程序員能夠將本身的平臺相關代碼庫加載到不一樣的 Java 虛擬機上。在某些狀況下,程序員可能不得不使用低層且與虛擬機有關的接口來得到較高的效率。但在其它狀況下,程序員可能使用高層接口來創建軟件組件。實際上,咱們但願隨着 Java 環境和組件軟件技術發展得愈來愈成熟,本地方法將變得愈來愈不重要。
利用 JNI 編程
本地方法程序設計人員應開始利用 JNI 進行編程。利用 JNI 編程隔離了一些未知條件,例如終端用戶可能正在運行的廠商的虛擬機。遵照 JNI 標準是本地庫能在給定 Java 虛擬機上運行的最好保證。例如,雖然 JDK 1.1 將繼續支持 JDK 1.0 中所實現的舊式的本地方法接口,可是能夠確定的是 JDK 的將來版本將中止支持舊式的本地方法接口。依賴於舊式接口的本地方法將不得不從新編寫。
若是您正在實現 Java 虛擬機,則應該實現 JNI。咱們(Javasoft 和得到許可方)盡力確保 JNI 不會佔用虛擬機實現的系統開銷或施加任何限制,包括對象表示,垃圾收集機制等。若是您遇到了咱們可能忽視了的問題,請告知咱們。
JDK 1.1.2 中的變化
爲了更好地支持 Java 運行時環境 (JRE),在 JDK 1.1.2 中對調用 API 在幾個方面做了擴展。這些變化沒有破壞任何已有代碼,JNI 本地方法接口也沒有改變。
name=value
表示系統屬性(該功能對應於 Java 命令行中的 -D 選項)。
本章着重討論 JNI 中的主要設計問題,其中的大部分問題都與本地方法有關。調用 API 的設計將在 第 5 章 「調用 API」 中討論。
平臺相關代碼是經過調用 JNI 函數來訪問 Java 虛擬機功能的。JNI 函數可經過接口指針來得到。接口指針是指針的指針,它指向一個指針數組,而指針數組中的每一個元素又指向一個接口函數。每一個接口函數都處在數組的某個預約偏移量中。圖 2-1 說明了接口指針的組織結構。
JNI 接口的組織相似於 C++ 虛擬函數表或 COM 接口。使用接口表而不使用硬性編入的函數表的好處是使 JNI 名字空間與平臺相關代碼分開。虛擬機能夠很容易地提供多個版本的 JNI 函數表。例如,虛擬機可支持如下兩個 JNI 函數表:
JNI 接口指針只在當前線程中有效。所以,本地方法不能將接口指針從一個線程傳遞到另外一個線程中。實現 JNI 的虛擬機可將本地線程的數據分配和儲存在 JNI 接口指針所指向的區域中。
本地方法將 JNI 接口指針看成參數來接受。虛擬機在從相同的 Java 線程中對本地方法進行屢次調用時,保證傳遞給該本地方法的接口指針是相同的。可是,一個本地方法可被不一樣的 Java 線程所調用,所以能夠接受不一樣的 JNI 接口指針。
對本地方法的加載經過 System.loadLibrary
方法實現。下例中,類初始化方法加載了一個與平臺有關的本地庫,在該本地庫中給出了本地方法 f
的定義:
package pkg;
class Cls {
native double f(int i, String s);
static {
System.loadLibrary("pkg_Cls");
}
}
System.loadLibrary 的參數是程序員任意選取的庫名。系統按照標準的但與平臺有關的處理方法將該庫名轉換爲本地庫名。例如,Solaris 系統將名稱 pkg_Cls
轉換爲 libpkg_Cls.so
,而 Win32 系統將相同的名稱 pkg_Cls
轉換爲 pkg_Cls.dll
。
程序員可用單個庫來存聽任意數量的類所需的全部本地方法,只要這些類將被相同的類加載器所加載。虛擬機在其內部爲每一個類加載器保護其所加載的本地庫清單。提供者應該儘可能選擇可以避免名稱衝突的本地庫名。
若是底層操做系統不支持動態連接,則必須事先將全部的本地方法連接到虛擬機上。這種狀況下,虛擬機實際上不須要加載庫便可完成 System.loadLibrary
調用。
程序員還可調用 JNI 函數 RegisterNatives()
來註冊與類關聯的本地方法。在與靜態連接的函數一塊兒使用時,RegisterNatives()
函數將特別有用。
動態連接程序是根據項的名稱來解析各項的。本地方法名由如下幾部分串接而成:
Java_
虛擬機將爲本地庫中的方法查找匹配的方法名。它首先查找短名(沒有參數簽名的名稱),而後再查找帶參數簽名的長名稱。只有當某個本地方法被另外一個本地方法重載時程序員纔有必要使用長名。但若是本地方法的名稱與非本地方法的名稱相同,則不會有問題。由於非本地方法(Java 方法)並不放在本地庫中。
下例中,沒必要用長名來連接本地方法 g
,由於另外一個方法 g
不是本地方法,於是它並不在本地庫中。
class Cls1 {
int g(int i);
native int g(double d);
}
咱們採起簡單的名字攪亂方案,以保證全部的 Unicode 字符都能被轉換爲有效的 C 函數名。咱們用下劃線(「_」) 字符來代替全限定的類名中的斜槓(「/」)。因爲名稱或類型描述符歷來不會以數字打頭,我們用 _0
、...、_9
來代替轉義字符序列,如表 2-1所示:
表 2-1 Unicode 字符轉換 |
|
轉義字符序列 |
表示 |
|
Unicode 字符 |
|
字符「_」 |
|
簽名中的字符「;」 |
|
簽名中的字符「[」 |
本地方法和接口 API 都要遵照給定平臺上的庫調用標準約定。例如,UNIX 系統使用 C 調用約定,而 Win32 系統使用 __stdcall。
JNI 接口指針是本地方法的第一個參數。其類型是 JNIEnv。第二個參數隨本地方法是靜態仍是非靜態而有所不一樣。非靜態本地方法的第二個參數是對對象的引用,而靜態本地方法的第二個參數是對其 Java 類的引用。
其他的參數對應於一般 Java 方法的參數。本地方法調用利用返回值將結果傳回調用程序中。第 3 章 「JNI 的類型和數據結構」 將描述 Java 類型和 C 類型之間的映射。
代碼示例 2-1 說明了如何用 C 函數來實現本地方法 f
。對本地方法 f
的聲明以下:
package pkg;
class Cls {
native double f(int i, String s);
...
}
具備長 mangled 名稱 Java_pkg_Cls_f_ILjava_lang_String_2
的 C 函數實現本地方法f
:
jdouble Java_pkg_Cls_f__ILjava_lang_String_2 (
JNIEnv *env, /* 接口指針 */
jobject obj, /* 「this」指針 */
jint i, /* 第一個參數 */
jstring s) /* 第二個參數 */
{
/* 取得 Java 字符串的 C 版本 */
const char *str = (*env)->GetStringUTFChars(env, s, 0);
/* 處理該字符串 */
...
/* 至此完成對 str 的處理 */
(*env)->ReleaseStringUTFChars(env, s, str);
return ...
}
注意,咱們老是用接口指針 env 來操做 Java 對象。可用 C++ 將此代碼寫得稍微簡潔一些,如代碼示例 2-2 所示:
extern "C" /* 指定 C 調用約定 */
jdouble Java_pkg_Cls_f__ILjava_lang_String_2 (
JNIEnv *env, /* 接口指針 */
jobject obj, /* 「this」指針 */
jint i, /* 第一個參數 */
jstring s) /* 第二個參數 */
{
const char *str = env->GetStringUTFChars(s, 0);
...
env->ReleaseStringUTFChars(s, str);
return ...
}
使用 C++ 後,源代碼變得更爲直接,且接口指針參數消失。可是,C++ 的內在機制與 C 的徹底同樣。在 C++ 中,JNI 函數被定義爲內聯成員函數,它們將擴展爲相應的 C 對應函數。
基本類型(如整型、字符型等)在 Java 和平臺相關代碼之間直接進行復制。而 Java 對象由引用來傳遞。虛擬機必須跟蹤傳到平臺相關代碼中的對象,以使這些對象不會被垃圾收集器釋放。反之,平臺相關代碼必須能用某種方式通知虛擬機它再也不須要那些對象,同時,垃圾收集器必須可以移走被平臺相關代碼引用過的對象。
JNI 將平臺相關代碼使用的對象引用分紅兩類:局部引用和全局引用。局部引用在本地方法調用期間有效,並在本地方法返回後被自動釋放掉。全局引用將一直有效,直到被顯式釋放。
對象是被做爲局部引用傳遞給本地方法的,由 JNI 函數返回的全部 Java 對象也都是局部引用。JNI 容許程序員從局部引用建立全局引用。要求 Java 對象的 JNI 函數既可接受全局引用也可接受局部引用。本地方法將局部引用或全局引用做爲結果返回。
大多數狀況下,程序員應該依靠虛擬機在本地方法返回後釋放全部局部引用。可是,有時程序員必須顯式釋放某個局部引用。例如,考慮如下的情形:
JNI 容許程序員在本地方法內的任何地方對局部引用進行手工刪除。爲確保程序員能夠手工釋放局部引用,JNI 函數將不能建立額外的局部引用,除非是這些 JNI 函數要做爲結果返回的引用。
局部引用僅在建立它們的線程中有效。本地方法不能將局部引用從一個線程傳遞到另外一個線程中。
爲了實現局部引用,Java 虛擬機爲每一個從 Java 到本地方法的控制轉換都建立了註冊服務程序。註冊服務程序將不可移動的局部引用映射爲 Java 對象,並防止這些對象被看成垃圾收集。全部傳給本地方法的 Java 對象(包括那些做爲 JNI 函數調用結果返回的對象)將被自動添加到註冊服務程序中。本地方法返回後,註冊服務程序將被刪除,其中的全部項均可以被看成垃圾來收集。
可用各類不一樣的方法來實現註冊服務程序,例如,使用表、連接列表或 hash 表來實現。雖然引用計數可用來避免註冊服務程序中有重複的項,但 JNI 實現不是必須檢測和消除重複的項。
注意,以保守方式掃描本地堆棧並不能如實地實現局部引用。平臺相關代碼可將局部引用儲存在全局或堆數據結構中。
JNI 提供了一大批用來訪問全局引用和局部引用的函數。這意味着不管虛擬機在內部如何表示 Java 對象,相同的本地方法實現都能工做。這就是爲何 JNI 可被各類各樣的虛擬機實現所支持的關鍵緣由。
通過不透明的引用來使用訪問函數的開銷比直接訪問 C 數據結構的開銷來得高。我們相信,大多數狀況下,Java 程序員使用本地方法是爲了完成一些重要任務,此時這種接口的開銷不是首要問題。
對於含有大量基本數據類型(如整數數組和字符串)的 Java 對象來講,這種開銷將高得不可接受 (考慮一下用於執行矢量和矩陣運算的本地方法的情形便知)。對 Java 數組進行迭代而且要經過函數調用取回數組的每一個元素,其效率是很是低的。
一個解決辦法是引入「釘住」概念,以使本地方法可以要求虛擬機釘住數組內容。然後,該本地方法將接受指向數值元素的直接指針。可是,這種方法包含如下兩個前提:
咱們將採起折衷方法來克服上述兩個問題。
首先,咱們提供了一套函數,用於在 Java 數組的一部分和本地內存緩衝之間複製基本類型數組元素。這些函數只有在本地方法只需訪問大型數組中的一小部分元素時才使用。
其次,程序員可用另外一套函數來取回數組元素的受約束版本。記住,這些函數可能要求 Java 虛擬機分配存儲空間和進行復制。虛擬機實現將決定這些函數是否真正複製該數組,以下所示:
最後,接口提供了一些函數,用以通知虛擬機本地方法已再也不須要訪問這些數組元素。當調用這些函數時,系統或者釋放數組,或者在原始數組與其不可移動副本之間進行協調並將副本釋放。
這種處理方法具備靈活性。垃圾收集器的算法可對每一個給定的數組分別做出複製或釘住的決定。例如,垃圾收集器可能複製小型對象而釘住大型對象。
JNI 實現必須確保多個線程中運行的本地方法可同時訪問同一數組。例如,JNI 能夠爲每一個被釘住的數組保留一個內部計數器,以便某個線程不會解開同時被另外一個線程釘住的數組。注意,JNI 沒必要將基本類型數組鎖住以專供某個本地方法訪問。同時從不一樣的線程對 Java 數組進行更新將致使不肯定的結果。
JNI 容許本地方法訪問 Java 對象的域或調用其方法。JNI 用符號名稱和類型簽名來識別方法和域。從名稱和簽名來定位域或對象的過程可分爲兩步。例如,爲調用類 cls 中的 f
方法,平臺相關代碼首先要得到方法 ID,以下所示:
jmethodID mid =
env->GetMethodID(cls, "f", "(ILjava/lang/String;)D");
而後,平臺相關代碼可重複使用該方法 ID 而無須再查找該方法,以下所示:
jdouble result = env->CallDoubleMethod(obj, mid, 10, str);
域 ID 或方法 ID 並不能防止虛擬機卸載生成該 ID 的類。該類被卸載以後,該方法 ID 或域 ID 亦變成無效。所以,若是平臺相關代碼要長時間使用某個方法 ID 或域 ID,則它必須確保:
JNI 對域 ID 和方法 ID 的內部實現並不施加任何限制。
JNI 不檢查諸如傳遞 NULL 指針或非法參數類型之類的編程錯誤。非法的參數類型包括諸如要用 Java 類對象時卻用了普通 Java 對象這樣的錯誤。JNI 不檢查這些編程錯誤的理由以下:
大多數 C 庫函數對編程錯誤不進行防範。例如,printf()
函數在接到一個無效地址時一般是引發運行錯而不是返回錯誤代碼。強迫 C 庫函數檢查全部可能的錯誤狀況將有可能引發這種檢查被重複進行--先是在用戶代碼中進行,而後又在庫函數中再次進行。
程序員不得將非法指針或錯誤類型的參數傳遞給 JNI 函數。不然,可能產生意想不到的後果,包括可能使系統狀態受損或使虛擬機崩潰。
JNI 容許本地方法拋出任何 Java 異常。本地方法也能夠處理突出的 Java 異常。未被處理的 Java 異常將被傳回虛擬機中。
一些 JNI 函數使用 Java 異常機制來報告錯誤狀況。大多數狀況下,JNI 函數經過返回錯誤代碼並拋出 Java 異常來報告錯誤狀況。錯誤代碼一般是特殊的返回值(如 NULL),這種特殊的返回值在正常返回值範圍以外。所以,程序員能夠:
ExceptionOccurred()
來得到異常對象,它含有對錯誤狀況的更詳細說明。在如下兩種狀況中,程序員須要先查出異常,而後才能檢查錯誤代碼:
ExceptionOccurred()
以檢查在執行 Java 方法期間可能發生的異常。ArrayIndexOutOfBoundsException
或 ArrayStoreException
。在全部其它狀況下,返回值若是不是錯誤代碼值就可確保沒有拋出異常。
在多個線程的狀況下,當前線程之外的其它線程可能會拋出異步異常。異步異常並不當即影響當前線程中平臺相關代碼的執行,直到出現下列狀況:
ExceptionOccurred()
顯式檢查同步異常或異步異常。注意,只有那些有可能拋出同步異常的 JNI 函數才檢查異步異常。
本地方法應在必要的地方(例如,在一個沒有其它異常檢查的緊密循環中)插入 ExceptionOccurred()
檢查以確保當前線程可在適當時間內對異步異常做出響應。
可用兩種方法來處理平臺相關代碼中的異常:
ExceptionClear()
來清除異常,而後執行本身的異常處理代碼。拋出了某個異常以後,平臺相關代碼必須先清除異常,而後才能進行其它的 JNI 調用。當有待定異常時,只有如下這些 JNI 函數可被安全地調用:ExceptionOccurred()、ExceptionDescribe()
和 ExceptionClear()
。ExceptionDescribe()
函數將打印有關待定異常的調試消息。
本章討論 JNI 如何將 Java 類型映射到本地 C 類型。
表 3-1 描述 Java 基本類型及其與計算機相關的本地等效類型。
表 3-1 基本類型和本地等效類型 |
||
Java 類型 |
本地類型 |
說明 |
boolean |
jboolean |
無符號,8 位 |
byte |
jbyte |
無符號,8 位 |
char |
jchar |
無符號,16 位 |
short |
jshort |
有符號,16 位 |
int |
jint |
有符號,32 位 |
long |
jlong |
有符號,64 位 |
float |
jfloat |
32 位 |
double |
jdouble |
64 位 |
void |
void |
N/A |
爲了使用方便,特提供如下定義。
#define JNI_FALSE 0
#define JNI_TRUE 1
jsize 整數類型用於描述主要指數和大小:
typedef jint jsize;
JNI 包含了不少對應於不一樣 Java 對象的引用類型。JNI 引用類型的組織層次如圖 3-1 所示。
在 C 中,全部其它 JNI 引用類型都被定義爲與 jobject 同樣。例如:
typedef jobject jclass;
在 C++ 中,JNI 引入了虛構類以增強子類關係。例如:
class _jobject {};
class _jclass : public _jobject {};
...
typedef _jobject *jobject;
typedef _jclass *jclass;
方法 ID 和域 ID 是常規的 C 指針類型:
struct _jfieldID; /*不透明結構 */
typedef struct _jfieldID *jfieldID; /* 域 ID */
struct _jmethodID; /* 不透明結構 */
typedef struct _jmethodID *jmethodID; /* 方法 ID */
jvalue 聯合類型在參數數組中用做單元類型。其聲明方式以下:
typedef union jvalue {
jboolean z;
jbyte b;
jchar c;
jshort s;
jint i;
jlong j;
jfloat f;
jdouble d;
jobject l;
} jvalue;
JNI 使用 Java 虛擬機的類型簽名表述。表 3-2 列出了這些類型簽名。
表 3-2 Java 虛擬機類型簽名 |
|
類型籤名 |
Java 類型 |
Z |
boolean |
B |
byte |
C |
char |
S |
short |
I |
int |
J |
long |
F |
float |
D |
double |
L fully-qualified-class ; |
全限定的類 |
[ type |
type[] |
( arg-types ) ret-type |
方法類型 |
例如,Java 方法:
long f (int n, String s, int[] arr);
具備如下類型簽名:
(ILjava/lang/String;[I)J
JNI 用 UTF-8 字符串來表示各類字符串類型。UTF-8 字符串和 Java 虛擬機所使用的同樣。UTF-8 字符串的編碼方式使得僅包含非空 ASCII 字符的字符序列可以按每字符一個字節表示,可是最多隻能表示 16 位的字符。全部在 \u0001
到 \u007F
範圍內的字符都用單字節表示,以下所示:
字節中的七位數據肯定了所表示字符的值。空字符 (\u000
) 和 \u0080
到 \u07FF
範圍內的字符用一對字節表示, 即 x 和 y,以下所示:
值爲 ((x&0x1f)<<6)+(y&0x3f)
的字符需用兩個字節表示。
\u0800
到 \uFFFF
範圍內的字符用三個字節表示,即 x,y,和 z:
值爲 ((x&0xf)<<12)+(y&0x3f)<<6)+(z&0x3f)
的字符需用三個字節表示。
此格式與「標準」 UTF-8 格式之間有兩個區別。第一,空字節 (byte)0
使用雙字節格式進行編碼,而不是單字節格式。這意味着 Java 虛擬機的 UTF-8 字符串不可能有嵌入的空值。第二,只使用單字節、雙字節和三字節格式。Java 虛擬機不能識別更長的 UTF-8 格式。
本章爲 JNI 函數提供參考信息。其中列出了所有 JNI 函數,同時也給出了 JNI 函數表的準確佈局。
注意:「必須」一詞用於約束 JNI 編程人員。例如,當說明某個 JNI 函數必須接收非空對象時,就應確保不要向該 JNI 函數傳遞 NULL。這時,JNI 實現將無需在該 JNI 函數中執行 NULL 指針檢查。
本章的部分資料改編自 Netscape 的 JRI 文檔。
該參考資料按用法對函數進行組織。參考部分按下列函數區域進行組織:
每一個函數都可經過 JNIEnv 參數以固定偏移量進行訪問。JNIEnv 的類型是一個指針,指向存儲所有 JNI 函數指針的結構。其定義以下:
注意:前三項留做未來與 COM 兼容。此外,咱們在函數表開頭部分也留出來多個 NULL
項,從而可將未來與類有關的 JNI 操做添加到 FindClass 後面,而非函數表的末尾。
注意,函數表可在全部 JNI 接口指針間共享。
代碼示例 4-1 |
const struct JNINativeInterface ... = { |
NULL, |
NULL, |
NULL, |
NULL, |
GetVersion, |
|
DefineClass, |
FindClass, |
NULL, |
NULL, |
NULL, |
GetSuperclass, |
IsAssignableFrom, |
NULL, |
|
Throw, |
ThrowNew, |
ExceptionOccurred, |
ExceptionDescribe, |
ExceptionClear, |
FatalError, |
NULL, |
NULL, |
|
NewGlobalRef, |
DeleteGlobalRef, |
DeleteLocalRef, |
IsSameObject, |
NULL, |
NULL, |
|
AllocObject, |
NewObject, |
NewObjectV, |
NewObjectA, |
|
GetObjectClass, |
IsInstanceOf, |
|
GetMethodID, |
|
CallObjectMethod, |
CallObjectMethodV, |
CallObjectMethodA, |
CallBooleanMethod, |
CallBooleanMethodV, |
CallBooleanMethodA, |
CallByteMethod, |
CallByteMethodV, |
CallByteMethodA, |
CallCharMethod, |
CallCharMethodV, |
CallCharMethodA, |
CallShortMethod, |
CallShortMethodV, |
CallShortMethodA, |
CallIntMethod, |
CallIntMethodV, |
CallIntMethodA, |
CallLongMethod, |
CallLongMethodV, |
CallLongMethodA, |
CallFloatMethod, |
CallFloatMethodV, |
CallFloatMethodA, |
CallDoubleMethod, |
CallDoubleMethodV, |
CallDoubleMethodA, |
CallVoidMethod, |
CallVoidMethodV, |
CallVoidMethodA, |
|
CallNonvirtualObjectMethod, |
CallNonvirtualObjectMethodV, |
CallNonvirtualObjectMethodA, |
CallNonvirtualBooleanMethod, |
CallNonvirtualBooleanMethodV, |
CallNonvirtualBooleanMethodA, |
CallNonvirtualByteMethod, |
CallNonvirtualByteMethodV, |
CallNonvirtualByteMethodA, |
CallNonvirtualCharMethod, |
CallNonvirtualCharMethodV, |
CallNonvirtualCharMethodA, |
CallNonvirtualShortMethod, |
CallNonvirtualShortMethodV, |
CallNonvirtualShortMethodA, |
CallNonvirtualIntMethod, |
CallNonvirtualIntMethodV, |
CallNonvirtualIntMethodA, |
CallNonvirtualLongMethod, |
CallNonvirtualLongMethodV, |
CallNonvirtualLongMethodA, |
CallNonvirtualFloatMethod, |
CallNonvirtualFloatMethodV, |
CallNonvirtualFloatMethodA, |
CallNonvirtualDoubleMethod, |
CallNonvirtualDoubleMethodV, |
CallNonvirtualDoubleMethodA, |
CallNonvirtualVoidMethod, |
CallNonvirtualVoidMethodV, |
CallNonvirtualVoidMethodA, |
|
GetFieldID, |
|
GetObjectField, |
GetBooleanField, |
GetByteField, |
GetCharField, |
GetShortField, |
GetIntField, |
GetLongField, |
GetFloatField, |
GetDoubleField, |
SetObjectField, |
SetBooleanField, |
SetByteField, |
SetCharField, |
SetShortField, |
SetIntField, |
SetLongField, |
SetFloatField, |
SetDoubleField, |
|
GetStaticMethodID, |
|
CallStaticObjectMethod, |
CallStaticObjectMethodV, |
CallStaticObjectMethodA, |
CallStaticBooleanMethod, |
CallStaticBooleanMethodV, |
CallStaticBooleanMethodA, |
CallStaticByteMethod, |
CallStaticByteMethodV, |
CallStaticByteMethodA, |
CallStaticCharMethod, |
CallStaticCharMethodV, |
CallStaticCharMethodA, |
CallStaticShortMethod, |
CallStaticShortMethodV, |
CallStaticShortMethodA, |
CallStaticIntMethod, |
CallStaticIntMethodV, |
CallStaticIntMethodA, |
CallStaticLongMethod, |
CallStaticLongMethodV, |
CallStaticLongMethodA, |
CallStaticFloatMethod, |
CallStaticFloatMethodV, |
CallStaticFloatMethodA, |
CallStaticDoubleMethod, |
CallStaticDoubleMethodV, |
CallStaticDoubleMethodA, |
CallStaticVoidMethod, |
CallStaticVoidMethodV, |
CallStaticVoidMethodA, |
|
GetStaticFieldID, |
|
GetStaticObjectField, |
GetStaticBooleanField, |
GetStaticByteField, |
GetStaticCharField, |
GetStaticShortField, |
GetStaticIntField, |
GetStaticLongField, |
GetStaticFloatField, |
GetStaticDoubleField, |
|
SetStaticObjectField, |
SetStaticBooleanField, |
SetStaticByteField, |
SetStaticCharField, |
SetStaticShortField, |
SetStaticIntField, |
SetStaticLongField, |
SetStaticFloatField, |
SetStaticDoubleField, |
|
NewString, |
GetStringLength, |
GetStringChars, |
ReleaseStringChars, |
|
NewStringUTF, |
GetStringUTFLength, |
GetStringUTFChars, |
ReleaseStringUTFChars, |
|
GetArrayLength, |
|
NewObjectArray, |
GetObjectArrayElement, |
SetObjectArrayElement, |
|
NewBooleanArray, |
NewByteArray, |
NewCharArray, |
NewShortArray, |
NewIntArray, |
NewLongArray, |
NewFloatArray, |
NewDoubleArray, |
|
GetBooleanArrayElements, |
GetByteArrayElements, |
GetCharArrayElements, |
GetShortArrayElements, |
GetIntArrayElements, |
GetLongArrayElements, |
GetFloatArrayElements, |
GetDoubleArrayElements, |
|
ReleaseBooleanArrayElements, |
ReleaseByteArrayElements, |
ReleaseCharArrayElements, |
ReleaseShortArrayElements, |
ReleaseIntArrayElements, |
ReleaseLongArrayElements, |
ReleaseFloatArrayElements, |
ReleaseDoubleArrayElements, |
|
GetBooleanArrayRegion, |
GetByteArrayRegion, |
GetCharArrayRegion, |
GetShortArrayRegion, |
GetIntArrayRegion, |
GetLongArrayRegion, |
GetFloatArrayRegion, |
GetDoubleArrayRegion, |
SetBooleanArrayRegion, |
SetByteArrayRegion, |
SetCharArrayRegion, |
SetShortArrayRegion, |
SetIntArrayRegion, |
SetLongArrayRegion, |
SetFloatArrayRegion, |
SetDoubleArrayRegion, |
|
RegisterNatives, |
UnregisterNatives, |
|
MonitorEnter, |
MonitorExit, |
|
GetJavaVM, |
}; |
jint GetVersion(JNIEnv *env);
返回本地方法接口的版本。
env:JNI 接口指針。
高 16 位返回主版本號,低 16 位返回次版本號。
在 JDK1.1 中,GetVersion()
返回 0x00010001。
jclass DefineClass(JNIEnv *env, jobject loader,
const jbyte *buf, jsize bufLen);
從原始類數據的緩衝區中加載類。
env:JNI 接口指針。
loader
:分派給所定義的類的類加載器。
buf
:包含 .class
文件數據的緩衝區。
bufLen
:緩衝區長度。
返回 Java 類對象。若是出錯則返回 NULL
。
ClassFormatError:若是類數據指定的類無效。
ClassCircularityError
:若是類或接口是自身的超類或超接口。
OutOfMemoryError
:若是系統內存不足。
jclass FindClass(JNIEnv *env, const char *name);
該函數用於加載本地定義的類。它將搜索由 CLASSPATH
環境變量爲具備指定名稱的類所指定的目錄和 zip 文件。
env:JNI 接口指針。
name
:類全名(即包名後跟類名,之間由「/
」分隔)。若是該名稱以「[
」(數組簽名字符)打頭,則返回一個數組類。
返回類對象全名。若是找不到該類,則返回 NULL
。
ClassFormatError:若是類數據指定的類無效。
ClassCircularityError
:若是類或接口是自身的超類或超接口。
NoClassDefFoundError
:若是找不到所請求的類或接口的定義。
OutOfMemoryError
:若是系統內存不足。
jclass GetSuperclass(JNIEnv *env, jclass clazz);
若是 clazz
表明類而非類 object
,則該函數返回由 clazz
所指定的類的超類。
若是 clazz
指定類 object
或表明某個接口,則該函數返回 NULL
。
env:JNI 接口指針。
clazz
:Java 類對象。
由 clazz
所表明的類的超類或 NULL
。
jboolean IsAssignableFrom(JNIEnv *env, jclass clazz1,
jclass clazz2);
肯定 clazz1
的對象是否可安全地強制轉換爲 clazz2
。
env:JNI 接口指針。
clazz1
:第一個類參數。
clazz2
:第二個類參數。
下列某個狀況爲真時返回 JNI_TRUE
:
jint Throw(JNIEnv *env, jthrowable obj);
拋出 java.lang.Throwable
對象。
env:JNI 接口指針。
obj
:java.lang.Throwable
對象。
成功時返回 0,失敗時返回負數。
java.lang.Throwable
對象 obj
。
jint ThrowNew(JNIEnv *env, jclass clazz,
const char *message);
利用指定類的消息(由 message
指定)構造異常對象並拋出該異常。
env:JNI 接口指針。
clazz
:java.lang.Throwable
的子類。
message
:用於構造 java.lang.Throwable
對象的消息。
成功時返回 0,失敗時返回負數。
新構造的 java.lang.Throwable
對象。
jthrowable ExceptionOccurred(JNIEnv *env);
肯定是否某個異常正被拋出。在平臺相關代碼調用 ExceptionClear()
或 Java 代碼處理該異常前,異常將始終保持拋出狀態。
env:JNI 接口指針。
返回正被拋出的異常對象,若是當前無異常被拋出,則返回 NULL
。
void ExceptionDescribe(JNIEnv *env);
將異常及堆棧的回溯輸出到系統錯誤報告信道(例如 stderr
)。該例程可便利調試操做。
env:JNI 接口指針。
void ExceptionClear(JNIEnv *env);
清除當前拋出的任何異常。若是當前無異常,則此例程不產生任何效果。
env:JNI 接口指針。
void FatalError(JNIEnv *env, const char *msg);
拋出致命錯誤而且不但願虛擬機進行修復。該函數無返回值。
env:JNI 接口指針。
msg
:錯誤消息。
jobject NewGlobalRef(JNIEnv *env, jobject obj);
建立 obj
參數所引用對象的新全局引用。obj
參數既能夠是全局引用,也能夠是局部引用。全局引用經過調用 DeleteGlobalRef()
來顯式撤消。
env:JNI 接口指針。
obj
:全局或局部引用。
返回全局引用。若是系統內存不足則返回 NULL
。
void DeleteGlobalRef(JNIEnv *env, jobject globalRef);
刪除 globalRef
所指向的全局引用。
env:JNI 接口指針。
globalRef
:全局引用。
void DeleteLocalRef(JNIEnv *env, jobject localRef);
刪除 localRef
所指向的局部引用。
env:JNI 接口指針。
localRef
:局部引用。
jobject AllocObject(JNIEnv *env, jclass clazz);
分配新 Java 對象而不調用該對象的任何構造函數。返回該對象的引用。
clazz 參數務必不要引用數組類。
env:JNI 接口指針。
clazz
:Java 類對象。
返回 Java 對象。若是沒法構造該對象,則返回 NULL
。
InstantiationException:若是該類爲一個接口或抽象類。
OutOfMemoryError
:若是系統內存不足。
jobject NewObject(JNIEnv *env, jclass clazz,
jmethodID methodID, ...);
jobject NewObjectA(JNIEnv *env, jclass clazz,
jmethodID methodID, jvalue *args);
jobject NewObjectV(JNIEnv *env, jclass clazz,
jmethodID methodID, va_list args);
構造新 Java 對象。方法 ID指示應調用的構造函數方法。該 ID 必須經過調用 GetMethodID()
得到,且調用時的方法名必須爲 <init>
,而返回類型必須爲 void
(V
)。
clazz
參數務必不要引用數組類。
編程人員應將傳遞給構造函數的全部參數緊跟着放在 methodID
參數的後面。NewObject()
收到這些參數後,將把它們傳給編程人員所要調用的 Java 方法。
編程人員應將傳遞給構造函數的全部參數放在 jvalues
類型的數組 args
中,該數組緊跟着放在 methodID
參數的後面。NewObject()
收到數組中的這些參數後,將把它們傳給編程人員所要調用的 Java 方法。
編程人員應將傳遞給構造函數的全部參數放在 va_list
類型的參數 args
中,該參數緊跟着放在 methodID
參數的後面。NewObject()
收到這些參數後,將把它們傳給編程人員所要調用的 Java 方法。
env:JNI 接口指針。
clazz
:Java 類對象。
methodID
:構造函數的方法 ID。
傳給構造函數的參數。
args:傳給構造函數的參數數組。
args:傳給構造函數的參數 va_list。
返回 Java 對象,若是沒法構造該對象,則返回 NULL
。
InstantiationException:若是該類爲接口或抽象類。
OutOfMemoryError
:若是系統內存不足。
構造函數拋出的任何異常。
jclass GetObjectClass(JNIEnv *env, jobject obj);
返回對象的類。
env:JNI 接口指針。
obj
:Java 對象(不能爲 NULL
)。
返回 Java 類對象。
jboolean IsInstanceOf(JNIEnv *env, jobject obj,
jclass clazz);
測試對象是否爲某個類的實例。
env:JNI 接口指針。
obj
:Java 對象。
clazz
:Java 類對象。
若是可將 obj
強制轉換爲 clazz
,則返回 JNI_TRUE
。不然返回 JNI_FALSE
。NULL
對象可強制轉換爲任何類。
jboolean IsSameObject(JNIEnv *env, jobject ref1,
jobject ref2);
測試兩個引用是否引用同一 Java 對象。
env:JNI 接口指針。
ref1
:Java 對象。
ref2
:Java 對象。
若是 ref1
和 ref2
引用同一 Java 對象或均爲 NULL
,則返回 JNI_TRUE
。不然返回 JNI_FALSE
。
jfieldID GetFieldID(JNIEnv *env, jclass clazz,
const char *name, const char *sig);
返回類的實例(非靜態)域的域 ID。該域由其名稱及簽名指定。訪問器函數的 Get<type>Field 及 Set<type>Field 系列使用域 ID 檢索對象域。
GetFieldID()
將未初始化的類初始化。
GetFieldID()
不能用於獲取數組的長度域。應使用 GetArrayLength()
。
env:JNI 接口指針。
clazz
:Java 類對象。
name
:
0 終結的 UTF-8 字符串中的域名。
sig
:0 終結的 UTF-8 字符串中的域簽名。
域 ID。若是操做失敗,則返回 NULL
。
NoSuchFieldError:若是找不到指定的域。
ExceptionInInitializerError
:若是因爲異常而致使類初始化程序失敗。
OutOfMemoryError
:若是系統內存不足。
NativeType
Get<type>Field(JNIEnv *env, jobject obj,
jfieldID fieldID);
該訪問器例程系列返回對象的實例(非靜態)域的值。要訪問的域由經過調用 GetFieldID()
而獲得的域 ID 指定。
下表說明了 Get<type>Field 例程名及結果類型。應將 Get<type>Field 中的 type 替換爲域的 Java 類型(或使用表中的某個實際例程名),而後將 NativeType 替換爲該例程對應的本地類型。
表 4-1 Get<type>Field 訪問器例程系列 |
|
Get<type>Field 例程名 |
本地類型 |
|
jobject |
|
jboolean |
|
jbyte |
|
jchar |
|
jshort |
|
jint |
|
jlong |
|
jfloat |
|
jdouble |
env:JNI 接口指針。
obj
:Java 對象(不能爲 NULL
)。
fieldID
:有效的域 ID。
域的內容。
void Set<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID,
NativeType value);
該訪問器例程系列設置對象的實例(非靜態)域的值。要訪問的域由經過調用 SetFieldID()
而獲得的域 ID 指定。
下表說明了 Set<type>Field 例程名及結果類型。應將 Set<type>Field 中的 type 替換爲域的 Java 類型(或使用表中的某個實際例程名),而後將 NativeType 替換爲該例程對應的本地類型。
表4-2 Set<type>Field 訪問器例程系列 |
|
Set<type>Field 例程名 |
本地類型 |
|
jobject |
S |
jboolean |
|
jbyte |
|
jchar |
|
jshort |
|
jint |
|
jlong |
|
jfloat |
|
jdouble |
env:JNI 接口指針。
obj
:Java 對象(不能爲 NULL
)。
fieldID
:有效的域 ID。
value
:域的新值。
jmethodID GetMethodID(JNIEnv *env, jclass clazz,
const char *name, const char *sig);
返回類或接口實例(非靜態)方法的方法 ID。方法可在某個 clazz
的超類中定義,也可從 clazz
繼承。該方法由其名稱和簽名決定。
GetMethodID()
可以使未初始化的類初始化。
要得到構造函數的方法 ID,應將 <init>
做爲方法名,同時將 void
(V
) 做爲返回類型。
env:JNI 接口指針。
clazz
:Java 類對象。
name
:0 終結的 UTF-8 字符串中的方法名。
sig
:0 終結的 UTF-8 字符串中的方法簽名。
方法 ID,若是找不到指定的方法,則爲 NULL
。
NoSuchMethodError:若是找不到指定方法。
ExceptionInInitializerError
:若是因爲異常而致使類初始化程序失敗。
OutOfMemoryError
:若是系統內存不足。
NativeType
Call<type>Method(JNIEnv *env, jobject obj,
jmethodID methodID, ...);
NativeType
Call<type>MethodA(JNIEnv *env, jobject obj,
jmethodID methodID, jvalue *args);
NativeType
Call<type>MethodV(JNIEnv *env, jobject obj,
jmethodID methodID, va_list args);
這三個操做的方法用於從本地方法調用 Java 實例方法。它們的差異僅在於向其所調用的方法傳遞參數時所用的機制。
這三個操做將根據所指定的方法 ID 調用 Java 對象的實例(非靜態)方法。
參數 methodID
必須經過調用 GetMethodID()
來得到。
當這些函數用於調用私有方法和構造函數時,方法 ID 必須從 obj
的真實類派生而來,而不該從其某個超類派生。
編程人員應將要傳給方法的全部參數緊跟着放在 methodID
參數以後。Call<type>Method 例程接受這些參數並將其傳給編程人員所要調用的 Java 方法。
編程人員應將要傳給方法的全部參數放在緊跟在 methodID
參數以後的 jvalues
類型數組 args 中。Call<type>MethodA routine 接受這些數組中的參數並將其傳給編程人員所要調用的 Java 方法。
編程人員將方法的全部參數放在緊跟着在 methodID
參數以後的 va_list
類型參數變量中。Call<type>MethodV routine 接受這些參數並將其傳給編程人員所要調用的 Java 方法。
下表根據結果類型說明了各個方法調用例程。用戶應將 Call<type>Method 中的 type 替換爲所調用方法的 Java 類型(或使用表中的實際方法調用例程名),同時將 NativeType 替換爲該例程相應的本地類型。
表 4-3 實例方法調用例程 |
|
Call<type>Method 例程名 |
本地類型 |
|
void |
|
jobject |
|
jboolean |
|
jbyte |
|
jchar |
|
jshort |
|
jint |
|
jlong |
|
jfloat |
|
jdouble |
env:JNI 接口指針。
obj
:Java 對象。
methodID
:方法 ID。
要傳給 Java 方法的參數。
args:參數數組。
args:參數的 va_list。
返回調用 Java 方法的結果。
執行 Java 方法時拋出的異常。
NativeType
CallNonvirtual<type>Method(JNIEnv *env, jobject obj,
jclass clazz, jmethodID methodID, ...);
NativeType
CallNonvirtual<type>MethodA(JNIEnv *env, jobject obj,
jclass clazz, jmethodID methodID, jvalue *args);
NativeType
CallNonvirtual<type>MethodV(JNIEnv *env, jobject obj,
jclass clazz, jmethodID methodID, va_list args);
這些操做根據指定的類和方法 ID 調用某 Java 對象的實例(非靜態)方法。參數 methodID
必須經過調用 clazz
類的 GetMethodID()
得到。
CallNonvirtual<type>Method 和 Call<type>Method 例程系列並不相同。Call<type>Method 例程根據對象的類調用方法,而 CallNonvirtual<type>Method 例程則根據得到方法 ID 的(由 clazz
參數指定)類調用方法。方法 ID 必須從對象的真實類或其某個超類得到。
編程人員應將要傳給方法的全部參數緊跟着放在 methodID
參數以後。CallNonvirtual<type>Method routine 接受這些參數並將其傳給編程人員所要調用的 Java 方法。
編程人員應將要傳給方法的全部參數放在緊跟在 methodID
參數以後的 jvalues
類型數組 args 中。CallNonvirtual<type>MethodA routine 接受這些數組中的參數並將其傳給編程人員所要調用的 Java 方法。
編程人員應將要傳給方法的全部參數放在緊跟在 methodID
參數以後的 va_list
類型參數 args 中。CallNonvirtualMethodV routine 接受這些參數並將其傳給編程人員所要調用的 Java 方法。
下表根據結果類型說明了各個方法調用例程。用戶應將 CallNonvirtual<type>Method 中的 type 替換爲所調用方法的 Java 類型(或使用表中的實際方法調用例程名),同時將 NativeType 替換爲該例程相應的本地類型。
表 4-4 CallNonvirtual<type>Method 例程 |
|
CallNonvirtual<type>Method 例程名 |
本地類型 |
|
void |
|
jobject |
|
jboolean |
|
jbyte |
|
jchar |
|
jshort |
|
jint |
|
jlong |
|
jfloat |
|
jdouble |
env:JNI 接口指針。
clazz:
Java 類。
obj
: Java 對象。
methodID
:方法 ID。
要傳給 Java 方法的參數。
args:參數數組。
args:參數的 va_list
。
執行 Java 方法時所拋出的異常。
jfieldID GetStaticFieldID(JNIEnv *env, jclass clazz,
const char *name, const char *sig);
返回類的靜態域的域 ID。域由其名稱和簽名指定。GetStatic<type>Field 和 SetStatic<type>Field 訪問器函數系列使用域 ID 檢索靜態域。
GetStaticFieldID()
將未初始化的類初始化。
env:JNI 接口指針。
clazz
:Java 類對象。
name
: 0 終結的 UTF-8 字符串中的靜態域名。
sig
:0 終結的 UTF-8 字符串中的域簽名。
域 ID。若是找不到指定的靜態域,則爲 NULL
。
NoSuchFieldError:若是找不到指定的靜態域。
ExceptionInInitializerError
:若是因爲異常而致使類初始化程序失敗。
OutOfMemoryError
:若是系統內存不足。
NativeType
GetStatic<type>Field(JNIEnv *env, jclass clazz,
jfieldID fieldID);
該訪問器例程系列返回對象的靜態域的值。要訪問的域由經過調用 GetStaticFieldID()
而獲得的域 ID 指定。
下表說明了 GetStatic<type>Field 例程名及結果類型。應將 GetStatic<type>Field 中的 type 替換爲域的 Java 類型(或使用表中的某個實際例程名),而後將 NativeType 替換爲該例程對應的本地類型。
表 4-5 GetStatic<type>Field 訪問器例程系列 |
|
GetStatic<type>Field 例程名 |
本地類型 |
|
jobject |
|
jboolean |
|
jbyte |
|
jchar |
|
jshort |
|
jint |
|
jlong |
|
jfloat |
|
jdouble |
env:JNI 接口指針。
clazz
:Java 類對象。
fieldID
:靜態域 ID。
靜態域的內容。
void SetStatic<type>Field(JNIEnv *env, jclass clazz,
jfieldID fieldID,
NativeType value);
該訪問器例程系列設置對象的靜態域的值。要訪問的域由經過調用 GetStaticFieldID()
而獲得的域 ID 指定。
下表說明了 SetStatic<type>Field 例程名及結果類型。應將 SetStatic<type>Field 中的 type 替換爲域的 Java 類型(或使用表中的某個實際例程名),而後將 NativeType 替換爲該例程對應的本地類型。
表4-6 SetStatic<type>Field 訪問器例程系列 |
|
SetStatic<type>Field 例程名 |
本地類型 |
|
jobject |
|
jboolean |
|
jbyte |
|
jchar |
|
jshort |
|
jint |
|
jlong |
|
jfloat |
|
jdouble |
env:JNI 接口指針。
clazz:Java 類對象。
fieldID
:靜態域 ID。
value
:域的新值。
jmethodID GetStaticMethodID(JNIEnv *env, jclass clazz,
const char *name, const char *sig);
返回類的靜態方法的方法 ID。方法由其名稱和簽名指定。
GetStaticMethodID()
將未初始化的類初始化。
env:JNI 接口指針。
clazz:Java 類對象。
name
:0 終結 UTF-8 字符串中的靜態方法名。
sig
:0 終結 UTF-8 字符串中的方法簽名。
方法 ID,若是操做失敗,則爲 NULL
。
NoSuchMethodError:若是找不到指定的靜態方法。
ExceptionInInitializerError
:若是因爲異常而致使類初始化程序失敗。
OutOfMemoryError
:若是系統內存不足。
NativeType
CallStatic<type>Method(JNIEnv *env, jclass clazz,
jmethodID methodID, ...);
NativeType
CallStatic<type>MethodA(JNIEnv *env, jclass clazz,
jmethodID methodID, jvalue *args);
NativeType
CallStatic<type>MethodV(JNIEnv *env, jclass clazz,
jmethodID methodID, va_list args);
這些操做將根據指定的方法 ID 調用 Java 對象的靜態方法。methodID
參數必須經過調用 GetStaticMethodID()
獲得。
方法 ID 必須從 clazz
派生,而不能從其超類派生。
編程人員應將要傳給方法的全部參數緊跟着放在 methodID
參數以後。 CallStatic<type>Method routine 接受這些參數並將其傳給編程人員所要調用的 Java 方法。
編程人員應將要傳給方法的全部參數放在緊跟在 methodID
參數以後的 jvalues
類型數組 args 中。CallStaticMethodA routine 接受這些數組中的參數並將其傳給編程人員所要調用的 Java 方法。
編程人員應將要傳給方法的全部參數放在緊跟在 methodID
參數以後的 va_list
類型參數 args 中。CallStaticMethodV routine 接受這些參數並將其傳給編程人員所要調用的 Java 方法。
下表根據結果類型說明了各個方法調用例程。用戶應將 CallStatic<type>Method 中的 type 替換爲所調用方法的 Java 類型(或使用表中的實際方法調用例程名),同時將 NativeType 替換爲該例程相應的本地類型。
表 4-7 CallStatic<type>Method 調用例程 |
|
CallStatic<type>Method 例程名 |
本地類型 |
|
void |
|
jobject |
|
jboolean |
|
jbyte |
|
jchar |
|
jshort |
|
jint |
|
jlong |
|
jfloat |
|
jdouble |
env:JNI 接口指針。
clazz:Java 類對象。
methodID
:靜態方法 ID。
要傳給靜態方法的參數。
args:參數數組。
args:參數的 va_list
。
返回調用靜態 Java 方法的結果。
執行 Java 方法時拋出的異常。
jstring NewString(JNIEnv *env, const jchar *unicodeChars,
jsize len);
利用 Unicode 字符數組構造新的 java.lang.String
對象。
env:JNI 接口指針。
unicodeChars
:指向 Unicode 字符串的指針。
len
:Unicode 字符串的長度。
Java 字符串對象。若是沒法構造該字符串,則爲 NULL
。
OutOfMemoryError:若是系統內存不足。
jsize GetStringLength(JNIEnv *env, jstring string);
返回 Java 字符串的長度(Unicode 字符數)。
env:JNI 接口指針。
string:Java 字符串對象。
Java 字符串的長度。
const jchar * GetStringChars(JNIEnv *env, jstring string,
jboolean *isCopy);
返回指向字符串的 Unicode 字符數組的指針。該指針在調用 ReleaseStringchars()
前一直有效。
若是 isCopy
非空,則在複製完成後將 *isCopy
設爲 JNI_TRUE
。若是沒有複製,則設爲 JNI_FALSE
。
env:JNI 接口指針。
string:Java 字符串對象。
isCopy
:指向布爾值的指針。
指向 Unicode 字符串的指針,若是操做失敗,則返回 NULL
。
void ReleaseStringChars(JNIEnv *env, jstring string,
const jchar *chars);
通知虛擬機平臺相關代碼無需再訪問 chars
。參數 chars
是一個指針,可經過 GetStringChars()
從 string
得到。
env:JNI 接口指針。
string:Java 字符串對象。
chars
:指向 Unicode 字符串的指針。
jstring NewStringUTF(JNIEnv *env, const char *bytes);
利用 UTF-8 字符數組構造新 java.lang.String
對象。
env:JNI 接口指針。若是沒法構造該字符串,則爲 NULL
。
bytes
:指向 UTF-8 字符串的指針。
Java 字符串對象。若是沒法構造該字符串,則爲 NULL
。
OutOfMemoryError:若是系統內存不足。
jsize GetStringUTFLength(JNIEnv *env, jstring string);
以字節爲單位返回字符串的 UTF-8 長度。
env:JNI 接口指針。
string:Java 字符串對象。
返回字符串的 UTF-8 長度。
const char* GetStringUTFChars(JNIEnv *env, jstring string,
jboolean *isCopy);
返回指向字符串的 UTF-8 字符數組的指針。該數組在被 ReleaseStringUTFChars()
釋放前將一直有效。
若是 isCopy
不是 NULL
,*isCopy
在複製完成後即被設爲 JNI_TRUE
。若是未複製,則設爲 JNI_FALSE
。
env:JNI 接口指針。
string:Java 字符串對象。
isCopy
:指向布爾值的指針。
指向 UTF-8 字符串的指針。若是操做失敗,則爲 NULL
。
void ReleaseStringUTFChars(JNIEnv *env, jstring string,
const char *utf);
通知虛擬機平臺相關代碼無需再訪問 utf
。utf
參數是一個指針,可利用 GetStringUTFChars()
從 string
得到。
env:JNI 接口指針。
string:Java 字符串對象。
utf
:指向 UTF-8 字符串的指針。
jsize GetArrayLength(JNIEnv *env, jarray array);
返回數組中的元素數。
env:JNI 接口指針。
array
:Java 數組對象。
數組的長度。
jarray NewObjectArray(JNIEnv *env, jsize length,
jclass elementClass, jobject initialElement);
構造新的數組,它將保存類 elementClass
中的對象。全部元素初始值均設爲 initialElement
。
env:JNI 接口指針。
length
:數組大小。
elementClass
:數組元素類。
initialElement
:初始值。
Java 數組對象。若是沒法構造數組,則爲 NULL
。
OutOfMemoryError:若是系統內存不足。
jobject GetObjectArrayElement(JNIEnv *env,
jobjectArray array, jsize index);
返回 Object
數組的元素。
env:JNI 接口指針。
array
:Java 數組。
index
:數組下標。
Java 對象。
ArrayIndexOutOfBoundsException:若是 index
不是數組中的有效下標。
void SetObjectArrayElement(JNIEnv *env, jobjectArray array,
jsize index, jobject value);
設置 Object
數組的元素。
env:JNI 接口指針。
array
:Java 數組。
index
:數組下標。
value
:新值。
ArrayIndexOutOfBoundsException:若是 index
不是數組中的有效下標。
ArrayStoreException
:若是 value
的類不是數組元素類的子類。
ArrayType
New<PrimitiveType>Array(JNIEnv *env, jsize length);
用於構造新基本類型數組對象的一系列操做。表 4-8 說明了特定的基本類型數組構造函數。用戶應把 New<PrimitiveType>Array 替換爲某個實際的基本類型數組構造函數例程名(見下表),而後將 ArrayType 替換爲該例程相應的數組類型。
表 4-8 New<PrimitiveType>Array 數組構造函數系列 |
|
New<PrimitiveType>Array 例程 |
數組類型 |
|
jbooleanArray |
|
jbyteArray |
|
jcharArray |
|
jshortArray |
|
jintArray |
|
jlongArray |
|
jfloatArray |
|
jdoubleArray |
env:JNI 接口指針。
length
:數組長度。
Java 數組。若是沒法構造該數組,則爲 NULL
。
NativeType *
Get<PrimitiveType>ArrayElements(JNIEnv *env,
ArrayType array, jboolean *isCopy);
一組返回基本類型數組體的函數。結果在調用相應的 Release<PrimitiveType>ArrayElements() 函數前將一直有效。因爲返回的數組多是 Java 數組的副本,所以對返回數組的更改沒必要在基本類型數組中反映出來,直到調用了 Release<PrimitiveType>ArrayElements()。
若是 isCopy
不是 NULL
,*isCopy
在複製完成後即被設爲 JNI_TRUE
。若是未複製,則設爲 JNI_FALSE
。
下表說明了特定的基本類型數組元素訪問器。應進行下列替換;
無論布爾數組在 Java 虛擬機中如何表示,GetBooleanArrayElements()
將始終返回一個 jbooleans
類型的指針,其中每一字節表明一個元素(開包表示)。內存中將確保全部其它類型的數組爲連續的。
表4-9 Get<PrimitiveType>ArrayElements 訪問器例程系列 |
||
Get<PrimitiveType>ArrayElements 例程 |
數組類型 |
本地類型 |
|
jbooleanArray |
jboolean |
|
jbyteArray |
jbyte |
|
jcharArray |
jchar |
|
jshortArray |
jshort |
|
jintArray |
jint |
|
jlongArray |
jlong |
|
jfloatArray |
jfloat |
|
jdoubleArray |
jdouble |
env:JNI 接口指針。
array
:Java 字符串對象。
isCopy
:指向布爾值的指針。
返回指向數組元素的指針,若是操做失敗,則爲 NULL
。
void Release<PrimitiveType>ArrayElements(JNIEnv *env,
ArrayType array,
NativeType *elems, jint mode);
通知虛擬機平臺相關代碼無需再訪問 elems
的一組函數。elems
參數是一個經過使用對應的 Get<
PrimitiveType>
ArrayElements() 函數由 array 導出的指針。必要時,該函數將把對 elems
的修改複製回基本類型數組。
mode
參數將提供有關如何釋放數組緩衝區的信息。若是 elems
不是 array
中數組元素的副本,mode
將無效。不然,mode
將具備下表所述的功能:
表 4-10 基本類型數組釋放模式 |
|
模式 |
動做 |
|
複製回內容並釋放 |
|
複製回內容但不釋放 |
|
釋放緩衝區但不復制回變化 |
多數狀況下,編程人員將把「0」傳給 mode
參數以確保固定的數組和複製的數組保持一致。其它選項可使編程人員進一步控制內存管理,但使用時務必慎重。
下表說明了構成基本類型數組撤消程序系列的特定例程。應進行以下替換;
表 4-11 Release<PrimitiveType>ArrayElements 數組例程系列 |
||
Release<PrimitiveType>ArrayElements 例程 |
數組類型 |
本地類型 |
|
jbooleanArray |
jboolean |
|
jbyteArray |
jbyte |
|
jcharArray |
jchar |
|
jshortArray |
jshort |
|
jintArray |
jint |
|
jlongArray |
jlong |
|
jfloatArray |
jfloat |
|
jdoubleArray |
jdouble |
env:JNI 接口指針。
array
:Java 數組對象。
elems
:指向數組元素的指針。
mode
:釋放模式。
void
Get<PrimitiveType>ArrayRegion(JNIEnv *env,
ArrayType array,
jsize start, jsize len,
NativeType *buf);
將基本類型數組某一區域複製到緩衝區中的一組函數。
下表說明了特定的基本類型數組元素訪問器。應進行以下替換:
表 4-12 Get<PrimitiveType>ArrayRegion 數組訪問器例程系列 |
||
Get<PrimitiveType>ArrayRegion 例程 |
數組類型 |
本地類型 |
|
jbooleanArray |
jboolean |
|
jbyteArray |
jbyte |
|
jcharArray |
jchar |
|
jshortArray |
jhort |
|
jintArray |
jint |
|
jlongArray |
jlong |
|
jfloatArray |
jloat |
|
jdoubleArray |
jdouble |
env:JNI 接口指針。
array
:Java 指針。
start
:起始下標。
len
:要複製的元素數。
buf
:目的緩衝區。
ArrayIndexOutOfBoundsException:若是區域中的某個下標無效。
void Set<PrimitiveType>ArrayRegion(JNIEnv *env,
ArrayType array,
jsize start, jsize len,
NativeType *buf);
將基本類型數組的某一區域從緩衝區中複製回來的一組函數。
下表說明了特定的基本類型數組元素訪問器。應進行以下替換:
表 4-13 Set<PrimitiveType>ArrayRegion 數組訪問器例程系列 |
||
Set<PrimitiveType>ArrayRegion 例程 |
數組類型 |
本地類型 |
|
jbooleanArray |
jboolean |
|
jbyteArray |
jbyte |
|
jcharArray |
jchar |
|
jshortArray |
jshort |
|
jintArray |
jint |
|
jlongArray |
jlong |
|
jfloatArray |
jfloat |
|
jdoubleArray |
jdouble |
env:JNI 接口指針。
array
: Java 數組。
start
:起始下標。
len
:要複製的元素數。
buf
:源緩衝區。
ArrayIndexOutOfBoundsException:若是區域中的某個下標無效。
jint RegisterNatives(JNIEnv *env, jclass clazz,
const JNINativeMethod *methods, jint nMethods);
向 clazz
參數指定的類註冊本地方法。methods
參數將指定 JNINativeMethod
結構的數組,其中包含本地方法的名稱、簽名和函數指針。nMethods
參數將指定數組中的本地方法數。JNINativeMethod
結構定義以下所示:
typedef struct {
char *name;
char *signature;
void *fnPtr;
} JNINativeMethod;
函數指針一般必須有下列簽名:
ReturnType (*fnPtr)(JNIEnv *env, jobject objectOrClass, ...);
env:JNI 接口指針。
clazz:Java 類對象。
methods
:類中的本地方法。
nMethods
:類中的本地方法數。
成功時返回 "0";失敗時返回負數。
NoSuchMethodError:若是找不到指定的方法或方法不是本地方法。
jint UnregisterNatives(JNIEnv *env, jclass clazz);
取消註冊類的本地方法。類將返回到連接或註冊了本地方法函數前的狀態。
該函數不該在常規平臺相關代碼中使用。相反,它能夠爲某些程序提供一種從新加載和從新連接本地庫的途徑。
env:JNI 接口指針。
clazz:Java 類對象。
成功時返回「0」;失敗時返回負數。
jint MonitorEnter(JNIEnv *env, jobject obj);
進入與 obj
所引用的基本 Java 對象相關聯的監視程序。
每一個 Java 對象都有一個相關聯的監視程序。若是當前線程已經擁有與 obj
相關聯的監視程序,它將使指示該線程進入監視程序次數的監視程序計數器增 1。若是與 obj
相關聯的監視程序並不是由某個線程所擁有,則當前線程將變爲該監視程序的全部者,同時將該監視程序的計數器設置爲 1。若是另外一個線程已擁有與 obj
關聯的監視程序,則在監視程序被釋放前當前線程將處於等待狀態。監視程序被釋放後,當前線程將嘗試從新得到全部權。
env:JNI 接口指針。
obj
:常規 Java 對象或類對象。
成功時返回「0」;失敗時返回負數。
jint MonitorExit(JNIEnv *env, jobject obj);
當前線程必須是與 obj
所引用的基本 Java 對象相關聯的監視程序的全部者。線程將使指示進入監視程序次數的計數器減 1。若是計數器的值變爲 0,當前線程釋放監視程序。
env:JNI 接口指針。
obj
:常規 Java 對象或類對象。
成功時返回「0」;失敗時返回負數。
jint GetJavaVM(JNIEnv *env, JavaVM **vm);
返回與當前線程相關聯的 Java 虛擬機接口(用於調用 API 中)。結果將放在第二個參數 vm
所指向的位置。
env:JNI 接口指針。
vm
:指向放置結果的位置的指針。
成功時返回「0」;失敗時返回負數。
調用 API 容許軟件廠商將 Java 虛擬機加載到任意的本地程序中。廠商能夠交付支持 Java 的應用程序,而沒必要連接 Java 虛擬機源代碼。
本章首先概述了調用 API。而後是全部調用 API 函數的引用頁。
若要加強 Java 虛擬機的嵌入性,能夠用幾種方式來擴展 JDK 1.1.2 中的調用 API。
如下代碼示例說明了如何使用調用 API 中的函數。在本例中,C++ 代碼建立 Java 虛擬機而且調用名爲 Main.test
的靜態方法。爲清楚起見,咱們略去了錯誤檢查。
#include <jni.h> /* 其中定義了全部的事項 */
...
JavaVM *jvm; /* 表示 Java 虛擬機*/
JNIEnv *env; /* 指向本地方法接口的指針 */
JDK1_1InitArgs vm_args; /* JDK 1.1 虛擬機初始化參數 */
vm_args.version = 0x00010001; /* 1.1.2 中新增的:虛擬機版本 */
/* 得到缺省的初始化參數而且設置類
* 路徑 */
JNI_GetDefaultJavaVMInitArgs(&vm_args);
vm_args.classpath = ...;
/* 加載並初始化 Java 虛擬機,返回 env 中的
* JNI 接口指針 */
JNI_CreateJavaVM(&jvm, &env, &vm_args);
/* 用 JNI 調用 Main.test 方法 */
jclass cls = env->FindClass("Main");
jmethodID mid = env->GetStaticMethodID(cls, "test", "(I)V");
env->CallStaticVoidMethod(cls, mid, 100);
/* 結束。*/
jvm->DestroyJavaVM();
本例使用了 API 中的三個函數。調用 API 容許本地應用程序用 JNI 接口指針來訪問虛擬機特性。其設計相似於 Netscape 的 JRI 嵌入式接口。
JNI_CreateJavaVM() 函數加載並初始化 Java 虛擬機,而後將指針返回到 JNI 接口指針。調用 JNI_CreateJavaVM()
的線程被看做主線程。
JNI 接口指針 (JNIEnv
) 僅在當前線程中有效。若是另外一個線程須要訪問 Java 虛擬機,則該線程首先必須調用 AttachCurrentThread()
以將自身鏈接到虛擬機而且得到 JNI 接口指針。鏈接到虛擬機以後,本地線程的工做方式就與在本地方法內運行的普通 Java 線程同樣了。本地線程保持與虛擬機的鏈接,直到調用 DetachCurrentThread()
時才斷開鏈接。
主線程不能本身斷開與虛擬機的鏈接。而是必須調用 DestroyJavaVM()
來卸載整個虛擬機。
虛擬機等到主線程成爲惟一的用戶線程時才真正地卸載。用戶線程包括 Java 線程和附加的本地線程。之因此存在這種限制是由於 Java 線程或附加的本地線程可能正佔用着系統資源,例如鎖,窗口等。虛擬機不能自動釋放這些資源。卸載虛擬機時,經過將主線程限制爲惟一的運行線程,使釋聽任意線程所佔用系統資源的負擔落到程序員身上。
不一樣的 Java 虛擬機實現可能會須要不一樣的初始化參數。很難提出適合於全部現有和未來的 Java 虛擬機的標準初始化結構。做爲一種折衷方式,咱們保留了第一個域 (version
) 來識別初始化結構的內容。嵌入到 JDK 1.1.2 中的本地應用程序必須將版本域設置爲 0x00010001
。儘管其它實現可能會忽略某些由 JDK 所支持的初始化參數,咱們仍然鼓勵虛擬機實現使用與 JDK 同樣的初始化結構。
0x80000000
到 0xFFFFFFFF
之間的版本號需保留,而且不爲任何虛擬機實現所識別。
如下代碼顯示了初始化 JDK 1.1.2 中的 Java 虛擬機所用的結構。
typedef struct JavaVMInitArgs {
/* 前兩個域在 JDK 1.1 中保留,並
在 JDK 1.1.2 中正式引入。*/
/* Java 虛擬機版本 */
jint version;
/* 系統屬性。*/
char **properties;
/* 是否檢查 Java 源文件與已編譯的類文件
*之間的新舊關係。*/
jint checkSource;
/* Java 建立的線程的最大本地堆棧大小。*/
jint nativeStackSize;
/* 最大 Java 堆棧大小。*/
jint javaStackSize;
/* 初始堆大小。*/
jint minHeapSize;
/* 最大堆大小。*/
jint maxHeapSize;
/* 控制是否校驗 Java 字節碼:
* 0 無,1 遠程加載的代碼,2 全部代碼。*/
jint verifyMode;
/* 類加載的本地目錄路徑。*/
const char *classpath;
/* 重定向全部虛擬機消息的函數的鉤子。*/
jint (*vfprintf)(FILE *fp, const char *format,
va_list args);
/* 虛擬機退出鉤子。*/
void (*exit)(jint code);
/* 虛擬機放棄鉤子。*/
void (*abort)();
/* 是否啓用類 GC。*/
jint enableClassGC;
/* GC 消息是否出現。*/
jint enableVerboseGC;
/* 是否容許異步 GC。*/
jint disableAsyncGC;
/* 三個保留的域。*/
jint reserved0;
jint reserved1;
jint reserved2;
} JDK1_1InitArgs;
在 JDK 1.1.2 中,初始化結構提供了鉤子,這樣在虛擬機終止時,本地應用程序能夠重定向虛擬機消息並得到控制權。
當本地線程與 JDK 1.1.2 中的 Java 虛擬機鏈接時,如下結構將做爲參數進行傳遞。實際上,本地線程與 JDK 1.1.2 鏈接時不須要任何參數。JDK1_1AttachArgs
結構僅由 C 編譯器的填充槽組成,而 C 編譯器不容許空結構。
typedef struct JDK1_1AttachArgs {
/*
* JDK 1.1 不須要任何參數來附加
* 本地線程。此處填充的做用是爲了知足不容許空結構的 C
* 編譯器的要求。
*/
void *__padding;
} JDK1_1AttachArgs;
JavaVM 類型是指向調用 API 函數表的指針。如下代碼示例顯示了這種函數表。
typedef const struct JNIInvokeInterface *JavaVM;
const struct JNIInvokeInterface ... = {
NULL,
NULL,
NULL,
DestroyJavaVM,
AttachCurrentThread,
DetachCurrentThread,
};
注意,JNI_GetDefaultJavaVMInitArgs()
、JNI_GetCreatedJavaVMs() 和 JNI_CreateJavaVM()
這三個調用 API 函數不是 JavaVM 函數表的一部分。沒必要先有 JavaVM
結構,就可使用這些函數。
jint JNI_GetDefaultJavaVMInitArgs(void *vm_args);
返回 Java 虛擬機的缺省配置。在調用該函數以前,平臺相關代碼必須將 vm_args->version
域設置爲它所指望虛擬機支持的 JNI 版本。在 JDK 1.1.2 中,必須將 vm_args->version
設置爲 0x00010001
。(JDK 1.1 不要求平臺相關代碼設置版本域。爲了向後兼容性,若是沒有設置版本域,則 JDK 1.1.2 假定所請求的版本爲 0x00010001。JDK 的將來版本將要求把版本域設置爲適當的值。) 該函數返回後,將把 vm_args->version
設置爲虛擬機支持的實際 JNI 版本。
vm_args:指向 VM-specific initialization
(特定於虛擬機的初始化)結構的指針,缺省參數填入該結構。
若是所請求的版本獲得支持,則返回「0」;若是所請求的版本未獲得支持,則返回負數。
jint JNI_GetCreatedJavaVMs(JavaVM **vmBuf, jsize bufLen,
jsize *nVMs);
返回全部已建立的 Java 虛擬機。將指向虛擬機的指針依據其建立順序寫入 vmBuf 緩衝區。最多寫入 bufLen 項。在 *nVMs 中返回所建立虛擬機的總數。
JDK 1.1 不支持在單個進程中建立多個虛擬機。
vmBuf:指向將放置虛擬機結構的緩衝區的指針。
bufLen
:緩衝區的長度。
nVMs
:指向整數的指針。
成功時返回「0」;失敗則返回負數。
jint JNI_CreateJavaVM(JavaVM **p_vm, JNIEnv **p_env,
void *vm_args);
加載並初始化 Java 虛擬機。當前線程成爲主線程。將 env
參數設置爲主線程的 JNI 接口指針。
JDK 1.1.2 不支持在單個進程中建立多個虛擬機。必須將 vm_args 中的版本域設置爲 0x00010001
。
p_vm:指向位置(其中放置所獲得的虛擬機結構)的指針。
p_env
:指向位置(其中放置主線程的 JNI 接口指針)的指針。
vm_args
: Java 虛擬機初始化參數。
成功時返回「0」;失敗則返回負數。
jint DestroyJavaVM(JavaVM *vm);
卸載 Java 虛擬機並回收資源。只有主線程可以卸載虛擬機。調用 DestroyJavaVM()
時,主線程必須是惟一的剩餘用戶線程。
vm:將銷燬的 Java 虛擬機。
成功時返回「0」;失敗則返回負數。
JDK 1.1.2 不支持卸載虛擬機。
jint AttachCurrentThread(JavaVM *vm, JNIEnv **p_env,
void *thr_args);
將當前線程鏈接到 Java 虛擬機。在 JNIEnv
參數中返回 JNI 接口指針。
試圖鏈接已經鏈接的線程將不執行任何操做。
本地線程不能同時鏈接到兩個 Java 虛擬機上。
vm:當前線程所要鏈接到的虛擬機。
p_env
:指向位置(其中放置當前線程的 JNI 接口指針)的指針。
thr_args
:特定於虛擬機的線程鏈接參數。
成功時返回「0」;失敗則返回負數。
jint DetachCurrentThread(JavaVM *vm);
斷開當前線程與 Java 虛擬機之間的鏈接。釋放該線程佔用的全部 Java 監視程序。通知全部等待該線程終止的 Java 線程。
主線程(即建立 Java 虛擬機的線程)不能斷開與虛擬機之間的鏈接。做爲替代,主線程必須調用 JNI_DestroyJavaVM()
來卸載整個虛擬機。
vm:當前線程將斷開鏈接的虛擬機。
成功時返回「0」;失敗則返回負數。