聲明 設計概述 JNI介面函數和指針 載入和鏈接本地方法 解析本地方法名 本地方法的參數 引用Java對象 全局和局部引用 實現局部引用 訪問Java對象 訪問基本類型數組 訪問域和方法 報告編程錯誤 Java異常 異常和錯誤代碼 非同步異常 異常的處理 JNI的類型和數據結構 基本類型 引用類型 域 ...
- 聲明
- 設計概述
- JNI介面函數和指針
- 載入和鏈接本地方法
- 解析本地方法名
- 本地方法的參數
- 引用Java對象
- 全局和局部引用
- 實現局部引用
- 訪問Java對象
- 訪問基本類型數組
- 訪問域和方法
- 報告編程錯誤
- Java異常
- JNI的類型和數據結構
- JNI函數
- 介面函數表
- 版本信息
- 調用實例方法
- CalltypeMethod常式
- CalltypeMethodA常式
- CalltypeMethodV 常式
- CallNonvirtualtypeMethod常式
- CallNonvirtualtypeMethodA常式
- CallNonvirtualtypeMethodV常式
- CallStatictypeMethod常式
- CallStatictypeMethodA常式
- CallStatictypeMethodV常式
- 註冊本地方法
- 監視程式操作
- Java虛擬機介面
- 調用API
- 概述
- 創建虛擬機
- 連接虛擬機
- 卸載虛擬機
- 初始化結構
- 調用API函數
- 總結
聲明
該篇文章完全引用自《JNI完全手冊》完整版,用來方便查詢查閱,同時作為該系列教程的基礎知識。感謝原文檔作者。
文檔所依賴的版本是比較低的,但是恰恰是低版本才能更容易上手學習。文檔也有些枯燥,適合開發中參考查詢和粗略概況性
的瀏覽掌握大局使用,也是下來幾篇的基礎性指導文檔。下來幾篇不會再解釋代碼簡單函數釋義,只會說重點,遇到不懂的來
這篇文章搜索函數名即可查閱函數詳情。
設計概述
JNI介面函數和指針
平臺相關代碼是通過調用JNI函數來訪問Java虛擬機功能的。JNI函數可通過介面指針來獲得。介面指針是指針的指針,它指向
一個指針數組,而指針數組中的每個元素又指向一個介面函數。每個介面函數都處在數組的某個預定偏移量中。下圖說明瞭接
口指針的組織結構。
JNI介面的組織類似於C++虛擬函數表或COM介面。使用介面表而不使用硬性編入的函數表的好處是使JNI名字空間與平臺相關代碼分開。虛擬機可以很容易地提供多個版本的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_
- mangled 全限定的類名
- 下劃線(“_”)分隔符
- mangled 方法名
- 對於重載的本地方法,加上兩個下劃線(“__”),後跟mangled參數簽名
虛擬機將為本地庫中的方法查找匹配的方法名。它首先查找短名(沒有參數簽名的名稱),然後再查找帶參數簽名的長名稱。只有當某個本地方法被另一個本地方法重載時程式員才有必要使用長名。但如果本地方法的名稱與非本地方法的名稱相同,則不會有問題。因為非本地方法(Java方法)並不放在本地庫中。下例中,不必用長名來鏈接本地方法g,因為另一個方法g不是本地方法,因而它並不在本地庫中。
class Cls1 { int g(int i); native int g(double d); }
我們採取簡單的名字攪亂方案,以保證所有的Unicode字元都能被轉換為有效的C函數名。我們用下劃線(“_”)字元來代替全限定的類名中的斜杠(“/”)。由於名稱或類型描述符從來不會以數字打頭,我們用 _0、…、_9 來代替轉義字元序列。
本地方法和介面API都要遵守給定平臺上的庫調用標準約定。例如,UNIX系統使用C調用約定,而Win32系統使用 __stdcall。
本地方法的參數
JNI介面指針是本地方法的第一個參數。其類型是JNIEnv。第二個參數隨本地方法是靜態還是非靜態而有所不同。非靜態本地方法的第二個參數是對對象的引用,而靜態本地方法的第二個參數是對其Java類的引用。其餘的參數對應於通常Java方法的參數。本地方法調用利用返回值將結果傳回調用程式中。下一章 “JNI的類型和數據結構” 將描述Java類型和C類型之間的映射。
代碼示例說明瞭如何用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++將此代碼寫得稍微簡潔一些,如代碼示例所示:
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和平臺相關代碼之間直接進行複製。而Java對象由引用來傳遞。虛擬機必須跟蹤傳到平臺相關代碼中的對象,以使這些對象不會被垃圾收集器釋放。反之,平臺相關代碼必須能用某種方式通知虛擬機它不再需要那些對象,同時,垃圾收集器必須能夠移走被平臺相關代碼引用過的對象。
全局和局部引用
JNI將平臺相關代碼使用的對象引用分成兩類:局部引用和全局引用。局部引用在本地方法調用期間有效,併在本地方法返回後被自動釋放掉。全局引用將一直有效,直到被顯式釋放。對象是被作為局部引用傳遞給本地方法的,由JNI函數返回的所有Java對象也都是局部引用。JNI允許程式員從局部引用創建全局引用。要求Java對象的JNI函數既可接受全局引用也可接受局部引用。本地方法將局部引用或全局引用作為結果返回。
大多數情況下,程式員應該依靠虛擬機在本地方法返回後釋放所有局部引用。但是,有時程式員必須顯式釋放某個局部引用。例如,考慮以下的情形:
-
本地方法要訪問一個大型Java對象,於是創建了對該Java對象的局部引用。然後,本地方法要在返回調用程式之前執行其它計算。對這個大型Java對象的局部引用將防止該對象被當作垃圾收集,即使在剩餘的運算中並不再需要該對象。
-
本地方法創建了大量的局部引用,但這些局部引用並不是要同時使用。由於虛擬機需要一定的空間來跟蹤每個局部引用,創建太多的局部引用將可能使系統耗盡記憶體。 例如,本地方法要在一個大型對象數組中迴圈,把取回的元素作為局部引用,併在每次迭代時對一個元素進行操作。每次迭代後,程式員不再需要對該數組元素的局部引用。
JNI允許程式員在本地方法內的任何地方對局部引用進行手工刪除。為確保程式員可以手工釋放局部引用,JNI函數將不能創建額外的局部引用,除非是這些JNI函數要作為結果返回的引用。局部引用僅在創建它們的線程中有效。本地方法不能將局部引用從一個線程傳遞到另一個線程中。
實現局部引用
為了實現局部引用,Java虛擬機為每個從Java到本地方法的控制轉換都創建了註冊服務程式。註冊服務程式將不可移動的局部引用映射為Java對象,並防止這些對象被當作垃圾收集。所有傳給本地方法的Java對象(包括那些作為JNI函數調用結果返回的對象)將被自動添加到註冊服務程式中。本地方法返回後,註冊服務程式將被刪除,其中的所有項都可以被當作垃圾來收集。可用各種不同的方法來實現註冊服務程式,例如,使用表、鏈接列表或hash表來實現。雖然引用計數可用來避免註冊服務程式中有重覆的項,但JNI實現不是必須檢測和消除重覆的項。註意,以保守方式掃描本地堆棧並不能如實地實現局部引用。平臺相關代碼可將局部引用儲存在全局或堆數據結構中。
訪問Java對象
JNI提供了一大批用來訪問全局引用和局部引用的函數。這意味著無論虛擬機在內部如何表示Java對象,相同的本地方法實現都能工作。這就是為什麼JNI可被各種各樣的虛擬機實現所支持的關鍵原因。通過不透明的引用來使用訪問函數的開銷比直接訪問C數據結構的開銷來得高。我們相信,大多數情況下,Java程式員使用本地方法是為了完成一些重要任務,此時這種介面的開銷不是首要問題。
訪問基本類型數組
對於含有大量基本數據類型(如整數數組和字元串)的Java對象來說,這種開銷將高得不可接受(考慮一下用於執行矢量和矩陣運算的本地方法的情形便知)。對Java數組進行迭代並且要通過函數調用取回數組的每個元素,其效率是非常低的。
一個解決辦法是引入“釘住”概念,以使本地方法能夠要求虛擬機釘住數組內容。而後,該本地方法將接受指向數值元素的直接指針。但是,這種方法包含以下兩個前提:
- 垃圾收集器必須支持釘住。
- 虛擬機必須在記憶體中連續存放基本類型數組。雖然大多數基本類型數組都是連續存放的,但布爾數組可以壓縮或不壓縮存儲。因此,依賴於布爾數組確切存儲方式的本地方法將是不可移植的。
我們將採取折衷方法來剋服上述兩個問題。
首先,我們提供了一套函數,用於在Java數組的一部分和本地記憶體緩衝之間複製基本類型數組元素。這些函數只有在本地方法只需訪問大型數組中的一小部分元素時才使用。
其次,程式員可用另一套函數來取回數組元素的受約束版本。記住,這些函數可能要求Java虛擬機分配存儲空間和進行複製。虛擬機實現將決定這些函數是否真正複製該數組,如下所示:
- 如果垃圾收集器支持釘住,且數組的佈局符合本地方法的要求,則不需要進行複製。
- 否則,該數組將被覆制到不可移動的記憶體塊中(例如,複製到C堆中),併進行必要的格式轉換,然後返回指向該副本的指針。
最後,介面提供了一些函數,用以通知虛擬機本地方法已不再需要訪問這些數組元素。當調用這些函數時,系統或者釋放數組,或者在原始數組與其不可移動副本之間進行協調並將副本釋放。
這種處理方法具有靈活性。垃圾收集器的演算法可對每個給定的數組分別作出複製或釘住的決定。例如,垃圾收集器可能複製小型對象而釘住大型對象。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,則它必須確保:保留對所涉及類的活引用,或重新計算該方法ID或域ID。JNI對域ID和方法ID的內部實現並不施加任何限制。
報告編程錯誤
JNI不檢查諸如傳遞NULL指針或非法參數類型之類的編程錯誤。非法的參數類型包括諸如要用Java類對象時卻用了普通Java對象這樣的錯誤。JNI不檢查這些編程錯誤的理由如下:
- 強迫JNI函數去檢查所有可能的錯誤情況將降低正常(正確)的本地方法的性能。
- 在許多情況下,沒有足夠的運行時的類型信息可供這種檢查使用。
大多數C庫函數對編程錯誤不進行防範。例如,printf()函數在接到一個無效地址時通常是引起運行錯而不是返回錯誤代碼。強迫C庫函數檢查所有可能的錯誤情況將有可能引起這種檢查被重覆進行–先是在用戶代碼中進行,然後又在庫函數中再次進行。
程式員不得將非法指針或錯誤類型的參數傳遞給JNI函數。否則,可能產生意想不到的後果,包括可能使系統狀態受損或使虛擬機崩潰。
Java異常
JNI允許本地方法拋出任何Java異常。本地方法也可以處理突出的Java異常。未被處理的Java異常將被傳回虛擬機中。
異常和錯誤代碼
一些JNI函數使用Java異常機制來報告錯誤情況。大多數情況下,JNI函數通過返回錯誤代碼並拋出Java異常來報告錯誤情況。錯誤代碼通常是特殊的返回值(如 NULL),這種特殊的返回值在正常返回值範圍之外。因此,程式員可以:快速檢查上一個JNI調用所返回的值以確定是否出錯,並通過調用函數ExceptionOccurred()來獲得異常對象,它含有對錯誤情況的更詳細說明。
在以下兩種情況中,程式員需要先查出異常,然後才能檢查錯誤代碼:
- 調用Java方法的JNI函數返回該Java方法的結果。程式員必須調用ExceptionOccurred() 以檢查在執行Java方法期間可能發生的異常。
- 某些用於訪問JNI數組的函數並不返回錯誤代碼,但可能會拋出ArrayIndexOutOfBoundsException或ArrayStoreException。
在所有其它情況下,返回值如果不是錯誤代碼值就可確保沒有拋出異常。
非同步異常
在多個線程的情況下,當前線程以外的其它線程可能會拋出非同步異常。非同步異常並不立即影響當前線程中平臺相關代碼的執行,直到出現下列情況:該平臺相關代碼調用某個有可能拋出同步異常的JNI函數,或者該平臺相關代碼用 ExceptionOccurred() 顯式檢查同步異常或非同步異常。
註意,只有那些有可能拋出同步異常的JNI函數才檢查非同步異常。本地方法應在必要的地方(例如,在一個沒有其它異常檢查的緊密迴圈中)插入ExceptionOccurred() 檢查以確保當前線程可在適當時間內對非同步異常作出響應。
異常的處理
可用兩種方法來處理平臺相關代碼中的異常:
- 本地方法可選擇立即返回,使異常在啟動該本地方法調用的Java代碼中拋出。
- 平臺相關代碼可通過調用ExceptionClear() 來清除異常,然後執行自己的異常處理代碼。
拋出了某個異常之後,平臺相關代碼必須先清除異常,然後才能進行其它的JNI調用。當有待定異常時,只有以下這些JNI函數可被安全地調用:ExceptionOccurred()、ExceptionDescribe()和ExceptionClear()。ExceptionDescribe()函數將列印有關待定異常的調試消息。
JNI的類型和數據結構
本章討論JNI如何將Java類型映射到本地C類型。
基本類型
基本類型和本地等效類型表:
Java類型 | 本地類型 | 說明 |
---|---|---|
boolean | jboolean | 無符號,8位 |
byte | jbyte | 無符號,8位 |
char | jchar | 無符號,16位 |
short | jshort | 有符號,16位 |
int | jint | 有符號,32位 |
long | jlong | 有符號,64位 |
float | jfloat | 32位 |
double | jdouble | |
void | void | N/A |
為了使用方便,特提供以下定義:
#define JNI_FALSE 0 #define JNI_TRUE 1
jsize整數類型用於描述主要指數和大小:
typedef jint jsize;
引用類型
JNI包含了很多對應於不同Java對象的引用類型。JNI引用類型的組織層次如圖所示:
在C中,所有其它JNI引用類型都被定義為與jobject一樣。例如:
typedef jobject jclass;
在C++中,JNI引入了虛構類以加強子類關係。例如:
class _jobject {}; class _jclass : public _jobject {}; ... typedef _jobject *jobject; typedef _jclass *jclass;
域ID和方法ID
方法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虛擬機的類型簽名錶述。下表列出了這些類型簽名:
類型簽名 | 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
UTF-8字元串
JNI用UTF-8字元串來表示各種字元串類型。UTF-8字元串和Java虛擬機所使用的一樣。UTF-8字元串的編碼方式使得僅包含非空ASCII字元的字元序列能夠按每字元一個位元組表示,但是最多只能表示16位的字元。所有在\u0001到\u007F範圍內的字元都用單位元組表示,如下所示:
|0|0-6位|
位元組中的七位數據確定了所表示字元的值。空字元 (\u000)和\u0080到\u07FF範圍內的字元用一對位元組表示, 即x和y,如下所示:
x:|1|1|0|6-10位| y:|1|0|0-5位|
值為((x&0x1f)<<6)+(y&0x3f)的字元需用兩個位元組表示。\u0800到\uFFFF範圍內的字元用三個位元組表示,即x,y和z:
x:|1|1|1|0|12-15位| y:|1|0|6-11位| z:|1|0|0-5位|
值為((x&0xf)<<12)+(y&0x3f)<<6)+(z&0x3f)的字元需用三個位元組表示。
此格式與“標準” UTF-8格式之間有兩個區別。第一,空位元組(byte)0使用雙位元組格式進行編碼,而不是單位元組格式。這意味著Java虛擬機的UTF-8字元串不可能有嵌入的空值。第二,只使用單位元組、雙位元組和三位元組格式。Java虛擬機不能識別更長的UTF-8格式。
JNI函數
本章為JNI函數提供參考信息。其中列出了全部JNI函數,同時也給出了JNI函數表的準確佈局。註意:“必須”一詞用於約束JNI編程人員。例如,當說明某個JNI函數必須接收非空對象時,就應確保不要向該JNI函數傳遞NULL。這時,JNI實現將無需在該JNI函數中執行NULL指針檢查。本章的部分資料改編自Netscape的JRI文檔。該參考資料按用法對函數進行組織。
介面函數表
每個函數均可通過JNIEnv參數以固定偏移量進行訪問。JNIEnv的類型是一個指針,指向存儲全部JNI函數指針的結構。註意:前三項留作將來與COM相容。此外,我們在函數表開頭部分也留出來多個NULL項,從而可將將來與類有關的JNI操作添加到FindClass後面,而非函數表的末尾。註意,函數表可在所有JNI介面指針間共用。
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, };
版本信息
GetVersion 返回本地方法介面的版本。
jint GetVersion(JNIEnv *env);
參數
env:JNI介面指針。
返回值:
高16位返回主版本號,低16位返回次版本號。
在JDK1.1 中,GetVersion()返回0x00010001。
類操作
DefineClass 從原始類數據的緩衝區中載入類。
jclass DefineClass(JNIEnv *env, jobject loader, const jbyte *buf, jsize bufLen);
參數:
env:JNI 介面指針。
loader:分派給所定義的類的類載入器。
buf:包含.class文件數據的緩衝區。
bufLen:緩衝區長度。
返回值:
返回Java類對象。如果出錯則返回NULL。
拋出:
ClassFormatError:如果類數據指定的類無效。
ClassCircularityError:如果類或介面是自身的超類或超介面。
OutOfMemoryError:如果系統記憶體不足。
FindClass 該函數用於載入本地定義的類。它將搜索由CLASSPATH環境變數為具有指定名稱的類所指定的目錄和zip文件。
jclass FindClass(JNIEnv *env, const char *name);
參數:
env:JNI介面指針。
name:類全名(即包名後跟類名,之間由“/”分隔)。如果該名稱以“[”(數組簽名字元)打頭,則返回一個數組類。
返回值:
返回類對象全名。如果找不到該類,則返回NULL。
拋出:
ClassFormatError:如果類數據指定的類無效。
ClassCircularityError:如果類或介面是自身的超類或超介面。
NoClassDefFoundError:如果找不到所請求的類或介面的定義。
OutOfMemoryError:如果系統記憶體不足。
GetSuperclass 如果clazz代表類而非類object,則該函數返回由clazz所指定的類的超類。如果clazz指定類object或代表某個介面,則該函數返回NULL。
jclass GetSuperclass(JNIEnv *env, jclass clazz);
參數:
env:JNI介面指針。
clazz:Java類對象。
返回值:
由clazz所代表的類的超類或NULL。
IsAssignableFrom 確定clazz1的對象是否可安全地強制轉換為clazz2。
jboolean IsAssignableFrom(JNIEnv *env, jclass clazz1, jclass clazz2);
參數:
env:JNI介面指針。
clazz1:第一個類參數。
clazz2:第二個類參數。
返回值:
下列某個情況為真時返回JNI_TRUE:
第一及第二個類參數引用同一個Java類。
第一個類是第二個類的子類。
第二個類是第一個類的某個介面。
異常
Throw 拋出java.lang.Throwable對象。
jint Throw(JNIEnv *env, jthrowable obj);
參數:
env:JNI介面指針。
obj:java.lang.Throwable對象。
返回值:
成功時返回0,失敗時返回負數。
拋出:
java.lang.Throwable對象obj。
ThrowNew利用指定類的消息(由message指定)構造異常對象並拋出該異常。
jint ThrowNew(JNIEnv *env, jclass clazz, const char *message);
參數:
env:JNI介面指針。
clazz:java.lang.Throwable的子類。
message:用於構造java.lang.Throwable對象的消息。
返回值:
成功時返回0,失敗時返回負數。
拋出:
新構造的java.lang.Throwable對象。
ExceptionOccurred 確定是否某個異常正被拋出。在平臺相關代碼調用ExceptionClear()或Java代碼處理該異常前,異常將始終保持拋出狀態。
jthrowable ExceptionOccurred(JNIEnv *env);
參數:
env:JNI介面指針。
返回值:
返回正被拋出的異常對象,如果當前無異常被拋出,則返回NULL。
ExceptionDescribe 將異常及堆棧的回溯輸出到系統錯誤報告通道(例如 stderr)。該常式可便利調試操作。
void ExceptionDescribe(JNIEnv *env);
參數:
env:JNI介面指針。
ExceptionClear 清除當前拋出的任何異常。如果當前無異常,則此常式不產生任何效果。
void ExceptionClear(JNIEnv *env);
參數:
env:JNI介面指針。
FatalError 拋出致命錯誤並且不希望虛擬機進行修複。該函數無返回值。
void FatalError(JNIEnv *env, const char *msg);
參數:
env:JNI介面指針。
msg:錯誤消息。
全局及局部引用
NewGlobalRef 創建obj參數所引用對象的新全局引用。obj參數既可以是全局引用,也可以是局部引用。全局引用通過調用DeleteGlobalRef()來顯式撤消。
jobject NewGlobalRef(JNIEnv *env, jobject obj);
參數:
env:JNI介面指針。
obj:全局或局部引用。
返回值:
返回全局引用。如果系統記憶體不足則返回NULL。
DeleteGlobalRef 刪除globalRef所指向的全局引用。
void DeleteGlobalRef(JNIEnv *env, jobject globalRef);
參數:
env:JNI介面指針。
globalRef:全局引用。
DeleteLocalRef 刪除localRef所指向的局部引用。
void DeleteLocalRef(JNIEnv *env, jobject localRef);
參數:
env:JNI介面指針。
localRef:局部引用。
對象操作
AllocObject 分配新Java對象而不調用該對象的任何構造函數。返回該對象的引用。clazz參數務必不要引用數組類。
jobject AllocObject(JNIEnv *env, jclass clazz);
參數:
env:JNI介面指針。
clazz:Java類對象。
返回值:
返回Java對象。如果無法構造該對象,則返回NULL。
拋出:
InstantiationException:如果該類為一個介面或抽象類。
OutOfMemoryError:如果系統記憶體不足。
NewObject NewObjectA NewObjectV 構造新Java對象。
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);
方法ID指示應調用的構造函數方法。該ID必須通過調用GetMethodID()獲得,且調用時的方法名必須為init,而返回類型必須為void(V)。clazz參數務必不要引用數組類。
NewObject
編程人員應將傳遞給構造函數的所有參數緊跟著放在methodID參數的後面。NewObject()收到這些參數後,將把它們傳給編程人員所要調用的Java方法。
NewObjectA
編程人員應將傳遞給構造函數的所有參數放在jvalues類型的數組args中,該數組緊跟著放在methodID參數的後面。NewObject()收到數組中的這些參數後,將把它們傳給編程人員所要調用的Java方法。
NewObjectV
編程人員應將傳遞給構造函數的所有參數放在va_list類型的參數args中,該參數緊跟著放在methodID參數的後面。NewObject()收到這些參數後,將把它們傳給編程人員所要調用的Java方法。
參數:
env:JNI介面指針。
clazz:Java類對象。
methodID:構造函數的方法ID。
NewObject 的其它參數:
傳給構造函數的參數。
NewObjectA的其它參數:
args:傳給構造函數的參數數組。
NewObjectV的其它參數:
args:傳給構造函數的參數va_list。
返回值:
返回Java對象,如果無法構造該對象,則返回NULL。
拋出:
InstantiationException:如果該類為介面或抽象類。
OutOfMemoryError:如果系統記憶體不足。
構造函數拋出的任何異常。
GetObjectClass 返回對象的類。
jclass GetObjectClass(JNIEnv *env, jobject obj);
參數:
env:JNI介面指針。
obj:Java對象(不能為NULL)。
返回值:
返回Java類對象。
IsInstanceOf 測試對象是否為某個類的實例。
jboolean IsInstanceOf(JNIEnv *env, jobject obj, jclass clazz);
參數:
env:JNI介面指針。
obj:Java對象。
clazz:Java類對象。
返回值:
如果可將obj強制轉換為clazz,則返回JNI_TRUE。否則返回JNI_FALSE。NULL對象可強制轉換為任何類。
IsSameObject 測試兩個引用是否引用同一Java對象。
jboolean IsSameObject(JNIEnv *env, jobject ref1, jobject ref2);
參數:
env:JNI介面指針。
ref1:Java對象。
ref2:Java對象。
返回值:
如果ref1和ref2引用同一Java對象或均為NULL,則返回JNI_TRUE。否則返回JNI_FALSE。
訪問對象的域
GetFieldID
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:如果系統記憶體不足。
Get[type]Field常式
NativeType Get[type]Field(JNIEnv *env, jobject obj, jfieldID fieldID);
該訪問器常式系列返回對象的實例(非靜態)域的值。要訪問的域由通過調用GetFieldID()而得到的域ID指定。下表說明瞭Get[type]Field常式名及結果類型。應將Get[type]Field中的type替換為域的Java類型(或使用表中的某個實際常式名),然後將NativeType替換為該常式對應的本地類型。
Get[type]Field常式名 | 本地類型 |
---|---|
GetObjectField() | jobject |
GetBooleanField() | jboolean |
GetByteField() | jbyte |
GetCharField() | jchar |
GetShortField() | jshort |
GetIntField() | jint |
GetLongField() | jlong |
GetFloatField() | jfloat |
GetDoubleField() | jdouble |
參數:
env:JNI介面指針。
obj:Java對象(不能為 NULL)。
fieldID:有效的域ID。
返回值:
域的內容。
Set[type]Field常式
void Set[type]Field(JNIEnv *env, jobject obj, jfieldID fieldID, NativeType value);
該訪問器常式系列設置對象的實例(非靜態)域的值。要訪問的域由通過調用SetFieldID()而得到的域ID指定。下表說明瞭Set[type]Field常式名及結果類型。應將Set[type]Field中的type替換為域的Java類型(或使用表中的某個實際常式名),然後將NativeType替換為該常式對應的本地類型。
Set[type]Field常式名 | 本地類型 |
---|---|
SetObjectField() | jobject |
SetBooleanField() | jboolean |
SetByteField() | jbyte |
SetCharField() | jchar |
SetShortField() | jshort |
SetIntField() | jint |
SetLongField() | jlong |
SetFloatField() | jfloat |
SetDoubleField() | jdouble |
參數:
env:JNI介面指針。
obj:Java對象(不能為NULL)。
fieldID:有效的域ID。
value:域的新值。
調用實例方法
GetMethodID
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:如果系統記憶體不足。
Call[type]Method,Call[type]MethodA,Call[type]MethodV常式
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的真實類派生而來,而不應從其某個超類派生。
Call[type]Method常式
編程人員應將要傳給方法的所有參數緊跟著放在methodID參數之後。Call[type]Method常式接受這些參數並將其傳給編程人員所要調用的Java方法。
Call[type]MethodA常式
編程人員應將要傳給方法的所有參數放在緊跟在methodID參數之後的jvalues類型數組args中。Call[type]MethodA routine接受這些數組中的參數並將其傳給編程人員所要調用的Java方法。
Call[type]MethodV 常式
編程人員將方法的所有參數放在緊跟著在methodID參數之後的va_list類型參數變數中。Call[type]MethodV routine接受這些參數並將其傳給編程人員所要調用的Java方法。
下表根據結果類型說明瞭各個方法調用常式。用戶應將Call[type]Method中的type替換為所調用方法的Java類型(或使用表中的實際方法調用常式名),同時將NativeType替換為該常式相應的本地類型。
Call[type]Method常式名 | 本地類型 |
---|---|
CallVoidMethod() CallVoidMethodA() CallVoidMethodV() | void |
CallObjectMethod() CallObjectMethodA() CallObjectMethodV() | jobject |
CallBooleanMethod() CallBooleanMethodA() CallBooleanMethodV() | jboolean |
CallByteMethod() CallByteMethodA() CallByteMethodV() | jbyte |
CallCharMethod() CallCharMethodA() CallCharMethodV() | jchar |
CallShortMethod() CallShortMethodA() CallShortMethodV() | jshort |
CallIntMethod() CallIntMethodA() CallIntMethodV() | jint |
CallLongMethod() CallLongMethodA() CallLongMethodV() | jlong |
CallFloatMethod() CallFloatMethodA() CallFloatMethodV() | jfloat |
CallDoubleMethod() CallDoubleMethodA() CallDoubleMethodV() | jdouble |
參數:
env:JNI介面指針。
obj:Java對象。
methodID:方法ID。
Call[type]Method常式的其它參數:
要傳給Java方法的參數。
Call[type]MethodA常式的其它參數:
args:參數數組。
Call[type]MethodV常式的其它參數:
args:參數的va_list。
返回值:
返回調用Java方法的結果。
拋出:
執行Java方法時拋出的異常。
CallNonvirtual[type]Method,CallNonvirtual[type]MethodA,CallNonvirtual[type]MethodV常式
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必須從對象的真實類或其某個超類獲得。
CallNonvirtual[type]Method常式
編程人員應將要傳給方法的所有參數緊跟著放在methodID參數之後。CallNonvirtual[type]Method routine接受這些參數並將其傳給編程人員所要調用的Java方法。
CallNonvirtual[type]MethodA常式
編程人員應將要傳給方法的所有參數放在緊跟在methodID參數之後的jvalues類型數組args中。
CallNonvirtual[type]MethodA routine接受這些數組中的參數並將其傳給編程人員所要調用的Java方法。
CallNonvirtual[type]MethodV常式
編程人員應將要傳給方法的所有參數放在緊跟在methodID參數之後的va_list類型參數args中。CallNonvirtualMethodV routine接受這些參數並將其傳給編程人員所要調用的Java方法。
下表根據結果類型說明瞭各個方法調用常式。用戶應將CallNonvirtual[type]Method中的type替換為所調用方法的Java類型(或使用表中的實際方法調用常式名),同時將NativeType替換為該常式相應的本地類型。
CallNonvirtual[type]Method常式名 | 本地類型 |
---|---|
CallNonvirtualVoidMethod() CallNonvirtualVoidMethodA() CallNonvirtualVoidMethodV() | void |
CallNonvirtualObjectMethod() CallNonvirtualObjectMethodA() CallNonvirtualObjectMethodV() | jobject |
CallNonvirtualBooleanMethod() CallNonvirtualBooleanMethodA() CallNonvirtualBooleanMethodV() | jboolean |
CallNonvirtualByteMethod() CallNonvirtualByteMethodA() CallNonvirtualByteMethodV() | jbyte |
CallNonvirtualCharMethod() CallNonvirtualCharMethodA() CallNonvirtualCharMethodV() | jchar |
CallNonvirtualShortMethod() CallNonvirtualShortMethodA() CallNonvirtualShortMethodV() | |
CallNonvirtualIntMethod() CallNonvirtualIntMethodA() CallNonvirtualIntMethodV() | jint |
CallNonvirtualLongMethod() CallNonvirtualLongMethodA() CallNonvirtualLongMethodV() | jlong |
CallNonvirtualFloatMethod() CallNonvirtualFloatMethodA() CallNonvirtualFloatMethodV() | jfloat |
CallNonvirtualDoubleMethod() CallNonvirtualDoubleMethodA() CallNonvirtualDoubleMethodV() | jdouble |
參數:
env:JNI介面指針。
clazz:Java類。
obj: Java對象。
methodID:方法ID。
CallNonvirtual[type]Method常式的其它參數:
要傳給Java方法的參數。
CallNonvirtual[type]MethodA常式的其它參數:
args:參數數組。
CallNonvirtual[type]MethodV常式的其它參數:
args:參數的va_list。
返回值:
調用Java方法的結果。
拋出:
執行Java方法時所拋出的異常。
訪問靜態域
GetStaticFieldID
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:如果系統記憶體不足。
GetStatic[type]Field常式
NativeType GetStatic[type]Field(JNIEnv *env, jclass clazz, jfieldID fieldID);
該訪問器常式系列返回對象的靜態域的值。要訪問的域由通過調用GetStaticFieldID()而得到的域ID指定。下表說明瞭GetStatic[type]Field常式名及結果類型。應將GetStatic[type]Field中的type替換為域的Java類型(或使用表中的某個實際常式名),然後將NativeType替換為該常式對應的本地類型。
GetStatic[type]Field常式名 | 本地類型 | |
---|---|---|
GetStaticObjectField() | jobject | |
GetStaticBooleanField() | jboolean | |
GetStaticByteField() | jbyte | |
GetStaticCharField() | jchar | |
GetStaticShortField() | jshort | |
GetStaticIntField() | jint | |
GetStaticLongField() | jlong | |
GetStaticFloatField() | jfloat | |
GetStaticDoubleField() | jdouble |
參數:
env:JNI介面指針。
clazz:Java類對象。
fieldID:靜態域ID。
返回值:
靜態域的內容。
SetStatic[type]Field常式
void SetStatic[type]Field(JNIEnv *env, jclass clazz, jfieldID fieldID, NativeType value);
該訪問器常式系列設置對象的靜態域的值。要訪問的域由通過調用GetStaticFieldID()而得到的域ID指定。下表說明瞭SetStatic[type]Field常式名及結果類型。應將SetStatic[type]Field中的type替換為域的Java類型(或使用表中的某個實際常式名),然後將NativeType替換為該常式對應的本地類型。
SetStatic[type]Field常式名 | 本地類型 |
---|---|
SetStaticObjectField() | jobject |
SetStaticBooleanField() | jboolean |
SetStaticByteField() | jbyte |
SetStaticCharField() | jchar |
SetStaticShortField() | jshort |
SetStaticIntField() | jint |
SetStaticLongField() | jlong |
SetStaticFloatField() | jfloat |
SetStaticDoubleField() | jdouble |
參數:
env:JNI介面指針。
clazz:Java類對象。
fieldID:靜態域ID。
value:域的新值。
調用靜態方法
GetStaticMethodID 返回類的靜態方法的方法ID。方法由其名稱和簽名指定。GetStaticMethodID()將未初始化的類初始化。
jmethodID GetStaticMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
參數:
env:JNI介面指針。
clazz:Java類對象。
name:0終結UTF-8字元串中的靜態方法名。
sig:0終結UTF-8字元串中的方法簽名。
返回值:
方法ID,如果操作失敗,則為NULL。
拋出:
NoSuchMethodError:如果找不到指定的靜態方法。
ExceptionInInitializerError:如果由於異常而導致類初始化程式失敗。
OutOfMemoryError:如果系統記憶體不足。