NDK - JNI官方中文資料

NDK-JNI官方中文資料

聲明

該篇文章完全引用自《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()函數將特別有用。

解析本地方法名

動態鏈接程序是根據項的名稱來解析各項的。本地方法名由以下幾部分串接而成:

  1. 前綴 Java_
  2. mangled 全限定的類名
  3. 下劃線(“_”)分隔符
  4. mangled 方法名
  5. 對於重載的本地方法,加上兩個下劃線(“__”),後跟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函數既可接受全局引用也可接受局部引用。本地方法 將局部引用或全局引用作爲結果返回。

大多數情況下,程序員應該依靠虛擬機在本地方法返回後釋放所有局部引用。但是,有時程序員必須顯式釋放某個局部引用。 例如,考慮以下的情形:

  1. 本地方法要訪問一個大型Java對象,於是創建了對該Java對象的局部引用。然後,本地方法要在返回調用程序之前執行其它計算。 對這個大型Java對象的局部引用將防止該對象被當作垃圾收集,即使在剩餘的運算中並不再需要該對象。

  2. 本地方法創建了大量的局部引用,但這些局部引用並不是要同時使用。由於虛擬機需要一定的空間來跟蹤每個局部引用, 創建太多的局部引用將可能使系統耗盡內存。 例如,本地方法要在一個大型對象數組中循環,把取回的元素作爲局部引用, 並在每次迭代時對一個元素進行操作。每次迭代後,程序員不再需要對該數組元素的局部引用。

JNI允許程序員在本地方法內的任何地方對局部引用進行手工刪除。爲確保程序員可以手工釋放局部引用,JNI函數將不能創建額外的 局部引用,除非是這些JNI函數要作爲結果返回的引用。局部引用僅在創建它們的線程中有效。本地方法不能將局部引用從一個線程 傳遞到另一個線程中。

實現局部引用

爲了實現局部引用,Java虛擬機爲每個從Java到本地方法的控制轉換都創建了註冊服務程序。註冊服務程序將不可移動的局部引用 映射爲Java對象,並防止這些對象被當作垃圾收集。所有傳給本地方法的Java對象(包括那些作爲JNI函數調用結果返回的對象)將 被自動添加到註冊服務程序中。本地方法返回後,註冊服務程序將被刪除,其中的所有項都可以被當作垃圾來收集。可用各種不同的 方法來實現註冊服務程序,例如,使用表、鏈接列表或hash表來實現。雖然引用計數可用來避免註冊服務程序中有重複的項,但JNI 實現不是必須檢測和消除重複的項。注意,以保守方式掃描本地堆棧並不能如實地實現局部引用。平臺相關代碼可將局部引用儲存在 全局或堆數據結構中。

訪問Java對象

JNI提供了一大批用來訪問全局引用和局部引用的函數。這意味着無論虛擬機在內部如何表示Java對象,相同的本地方法實現都能 工作。這就是爲什麼JNI可被各種各樣的虛擬機實現所支持的關鍵原因。通過不透明的引用來使用訪問函數的開銷比直接訪問C數據 結構的開銷來得高。我們相信,大多數情況下,Java程序員使用本地方法是爲了完成一些重要任務,此時這種接口的開銷不是首要 問題。

訪問基本類型數組

對於含有大量基本數據類型(如整數數組和字符串)的Java對象來說,這種開銷將高得不可接受(考慮一下用於執行矢量和矩陣運算 的本地方法的情形便知)。對Java數組進行迭代並且要通過函數調用取回數組的每個元素,其效率是非常低的。

一個解決辦法是引入“釘住”概念,以使本地方法能夠要求虛擬機釘住數組內容。而後,該本地方法將接受指向數值元素的直接指針。 但是,這種方法包含以下兩個前提:

  1. 垃圾收集器必須支持釘住。
  2. 虛擬機必須在內存中連續存放基本類型數組。雖然大多數基本類型數組都是連續存放的,但布爾數組可以壓縮或不壓縮存儲。因此, 依賴於布爾數組確切存儲方式的本地方法將是不可移植的。

我們將採取折衷方法來克服上述兩個問題。

首先,我們提供了一套函數,用於在Java數組的一部分和本地內存緩衝之間複製基本類型數組元素。這些函數只有在本地方法只需訪問 大型數組中的一小部分元素時才使用。

其次,程序員可用另一套函數來取回數組元素的受約束版本。記住,這些函數可能要求Java虛擬機分配存儲空間和進行復制。 虛擬機實現將決定這些函數是否真正複製該數組,如下所示:

  1. 如果垃圾收集器支持釘住,且數組的佈局符合本地方法的要求,則不需要進行復制。
  2. 否則,該數組將被複制到不可移動的內存塊中(例如,複製到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不檢查這些編程錯誤的理由如下:

  1. 強迫JNI函數去檢查所有可能的錯誤情況將降低正常(正確)的本地方法的性能。
  2. 在許多情況下,沒有足夠的運行時的類型信息可供這種檢查使用。

大多數C庫函數對編程錯誤不進行防範。例如,printf()函數在接到一個無效地址時通常是引起運行錯而不是返回錯誤代碼。強迫C庫 函數檢查所有可能的錯誤情況將有可能引起這種檢查被重複進行–先是在用戶代碼中進行,然後又在庫函數中再次進行。

程序員不得將非法指針或錯誤類型的參數傳遞給JNI函數。否則,可能產生意想不到的後果,包括可能使系統狀態受損或使虛擬機崩潰。

Java異常

JNI允許本地方法拋出任何Java異常。本地方法也可以處理突出的Java異常。未被處理的Java異常將被傳回虛擬機中。

異常和錯誤代碼

一些JNI函數使用Java異常機制來報告錯誤情況。大多數情況下,JNI函數通過返回錯誤代碼並拋出Java異常來報告錯誤情況。 錯誤代碼通常是特殊的返回值(如 NULL),這種特殊的返回值在正常返回值範圍之外。因此,程序員可以:快速檢查上一個 JNI調用所返回的值以確定是否出錯,並通過調用函數ExceptionOccurred()來獲得異常對象,它含有對錯誤情況的更詳細說明。

在以下兩種情況中,程序員需要先查出異常,然後才能檢查錯誤代碼:

  1. 調用Java方法的JNI函數返回該Java方法的結果。程序員必須調用ExceptionOccurred() 以檢查在執行Java方法期間可能發生的異常。
  2. 某些用於訪問JNI數組的函數並不返回錯誤代碼,但可能會拋出ArrayIndexOutOfBoundsException或ArrayStoreException。

在所有其它情況下,返回值如果不是錯誤代碼值就可確保沒有拋出異常。

異步異常

在多個線程的情況下,當前線程以外的其它線程可能會拋出異步異常。異步異常並不立即影響當前線程中平臺相關代碼的執行, 直到出現下列情況:該平臺相關代碼調用某個有可能拋出同步異常的JNI函數,或者該平臺相關代碼用 ExceptionOccurred() 顯式檢查同步異常或異步異常。

注意,只有那些有可能拋出同步異常的JNI函數才檢查異步異常。本地方法應在必要的地方(例如,在一個沒有其它異常檢查的 緊密循環中)插入ExceptionOccurred() 檢查以確保當前線程可在適當時間內對異步異常作出響應。

異常的處理

可用兩種方法來處理平臺相關代碼中的異常:

  1. 本地方法可選擇立即返回,使異常在啓動該本地方法調用的Java代碼中拋出。
  2. 平臺相關代碼可通過調用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 64位 
 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()獲得,且調用時的方法名必須爲, 而返回類型必須爲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。該域由其名稱及簽名指定。訪問器函數的GetField及SetField系列使用域ID檢索 對象域。GetFieldID()將未初始化的類初始化。GetFieldID()不能用於獲取數組的長度域。應使用GetArrayLength()。

參數: env:JNI接口指針。 clazz:Java類對象。 name: 0終結的UTF-8字符串中的域名。 sig:0終結的UTF-8字符串中的域簽名。

返回值: 域ID。如果操作失敗,則返回NULL。

拋出: NoSuchFieldError:如果找不到指定的域。 ExceptionInInitializerError:如果由於異常而導致類初始化程序失敗。 OutOfMemoryError:如果系統內存不足。

GetField例程

NativeType Get<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID);

該訪問器例程系列返回對象的實例(非靜態)域的值。要訪問的域由通過調用GetFieldID()而得到的域ID指定。下表說明了 GetField例程名及結果類型。應將GetField中的type替換爲域的Java類型(或使用表中的某個實際例程名), 然後將NativeType替換爲該例程對應的本地類型。

 *GetField例程名* 本地類型 
 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。

返回值: 域的內容。

SetField例程

void Set<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID, NativeType value);

該訪問器例程系列設置對象的實例(非靜態)域的值。要訪問的域由通過調用SetFieldID()而得到的域ID指定。下表說明了 SetField例程名及結果類型。應將SetField中的type替換爲域的Java類型(或使用表中的某個實際例程名), 然後將NativeType替換爲該例程對應的本地類型。

 *SetField例程名* 本地類型 
 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,應將作爲方法名,同時將void (V)作爲返回類型。

參數: env:JNI接口指針。 clazz:Java類對象。 name:0終結的UTF-8字符串中的方法名。 sig:0終結的UTF-8字符串中的方法簽名。

返回值: 方法ID,如果找不到指定的方法,則爲NULL。

拋出: NoSuchMethodError:如果找不到指定方法。 ExceptionInInitializerError:如果由於異常而導致類初始化程序失敗。 OutOfMemoryError:如果系統內存不足。

CallMethod,CallMethodA,CallMethodV例程

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的真實類派生而來,而不應從其某個超類派生。

CallMethod例程

編程人員應將要傳給方法的所有參數緊跟着放在methodID參數之後。CallMethod例程接受這些參數並將其傳給編程人員所要 調用的Java方法。

CallMethodA例程

編程人員應將要傳給方法的所有參數放在緊跟在methodID參數之後的jvalues類型數組args中。CallMethodA routine接受這些 數組中的參數並將其傳給編程人員所要調用的Java方法。

CallMethodV 例程

編程人員將方法的所有參數放在緊跟着在methodID參數之後的va_list類型參數變量中。CallMethodV routine接受這些參數 並將其傳給編程人員所要調用的Java方法。

下表根據結果類型說明了各個方法調用例程。用戶應將CallMethod中的type替換爲所調用方法的Java類型(或使用表中的實際 方法調用例程名),同時將NativeType替換爲該例程相應的本地類型。

 *CallMethod例程名* 本地類型 
 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。

CallMethod例程的其它參數: 要傳給Java方法的參數。

CallMethodA例程的其它參數: args:參數數組。

CallMethodV例程的其它參數: args:參數的va_list。

返回值: 返回調用Java方法的結果。

拋出: 執行Java方法時拋出的異常。

CallNonvirtualMethod,CallNonvirtualMethodA,CallNonvirtualMethodV例程

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()獲得。 CallNonvirtualMethod和CallMethod例程系列並不相同。CallMethod例程根據對象的類調用方法,而 CallNonvirtualMethod例程則根據獲得方法ID的(由clazz參數指定)類調用方法。方法ID必須從對象的真實類或其某個超類獲得。

CallNonvirtualMethod例程

編程人員應將要傳給方法的所有參數緊跟着放在methodID參數之後。CallNonvirtualMethod routine接受這些參數並將其傳給編 程人員所要調用的Java方法。

CallNonvirtualMethodA例程

編程人員應將要傳給方法的所有參數放在緊跟在methodID參數之後的jvalues類型數組args中。 CallNonvirtualMethodA routine接受這些數組中的參數並將其傳給編程人員所要調用的Java方法。

CallNonvirtualMethodV例程

編程人員應將要傳給方法的所有參數放在緊跟在methodID參數之後的va_list類型參數args中。CallNonvirtualMethodV routine接受 這些參數並將其傳給編程人員所要調用的Java方法。

下表根據結果類型說明了各個方法調用例程。用戶應將CallNonvirtualMethod中的type替換爲所調用方法的Java類型(或使用 表中的實際方法調用例程名),同時將NativeType替換爲該例程相應的本地類型。

 *CallNonvirtualMethod例程名* 本地類型 
 CallNonvirtualVoidMethod() CallNonvirtualVoidMethodA() CallNonvirtualVoidMethodV() void 
 CallNonvirtualObjectMethod() CallNonvirtualObjectMethodA() CallNonvirtualObjectMethodV() jobject 
 CallNonvirtualBooleanMethod() CallNonvirtualBooleanMethodA() CallNonvirtualBooleanMethodV() jboolean 
 CallNonvirtualByteMethod() CallNonvirtualByteMethodA() CallNonvirtualByteMethodV() jbyte 
 CallNonvirtualCharMethod() CallNonvirtualCharMethodA() CallNonvirtualCharMethodV() jchar 
 CallNonvirtualShortMethod() CallNonvirtualShortMethodA() CallNonvirtualShortMethodV() jshort 
 CallNonvirtualIntMethod() CallNonvirtualIntMethodA() CallNonvirtualIntMethodV() jint 
 CallNonvirtualLongMethod() CallNonvirtualLongMethodA() CallNonvirtualLongMethodV() jlong 
 CallNonvirtualFloatMethod() CallNonvirtualFloatMethodA() CallNonvirtualFloatMethodV() jfloat 
 CallNonvirtualDoubleMethod() CallNonvirtualDoubleMethodA() CallNonvirtualDoubleMethodV() jdouble 

參數: env:JNI接口指針。 clazz:Java類。 obj: Java對象。 methodID:方法ID。

CallNonvirtualMethod例程的其它參數: 要傳給Java方法的參數。

CallNonvirtualMethodA例程的其它參數: args:參數數組。

CallNonvirtualMethodV例程的其它參數: args:參數的va_list。

返回值: 調用Java方法的結果。

拋出: 執行Java方法時所拋出的異常。

訪問靜態域

GetStaticFieldID

jfieldID GetStaticFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig);

返回類的靜態域的域ID。域由其名稱和簽名指定。GetStaticField和SetStaticField訪問器函數系列使用域ID檢索 靜態域。GetStaticFieldID()將未初始化的類初始化。

參數: env:JNI接口指針。 clazz:Java類對象。 name: 0終結的UTF-8字符串中的靜態域名。 sig:0終結的UTF-8字符串中的域簽名。

返回值: 域ID。如果找不到指定的靜態域,則爲NULL。

拋出: NoSuchFieldError:如果找不到指定的靜態域。 ExceptionInInitializerError:如果由於異常而導致類初始化程序失敗。 OutOfMemoryError:如果系統內存不足。

GetStaticField例程

NativeType GetStatic<type>Field(JNIEnv *env, jclass clazz, jfieldID fieldID);

該訪問器例程系列返回對象的靜態域的值。要訪問的域由通過調用GetStaticFieldID()而得到的域ID指定。下表說明了 GetStaticField例程名及結果類型。應將GetStaticField中的type替換爲域的Java類型(或使用表中的某個 實際例程名),然後將NativeType替換爲該例程對應的本地類型。

 *GetStaticField例程名* 本地類型 
 GetStaticObjectField() jobject 
 GetStaticBooleanField() jboolean 
 GetStaticByteField() jbyte 
 GetStaticCharField() jchar 
 GetStaticShortField() jshort 
 GetStaticIntField() jint 
 GetStaticLongField() jlong 
 GetStaticFloatField() jfloat 
 GetStaticDoubleField() jdouble 

參數: env:JNI接口指針。 clazz:Java類對象。 fieldID:靜態域ID。 返回值: 靜態域的內容。

SetStaticField例程

void SetStatic<type>Field(JNIEnv *env, jclass clazz, jfieldID fieldID, NativeType value);

該訪問器例程系列設置對象的靜態域的值。要訪問的域由通過調用GetStaticFieldID()而得到的域ID指定。下表說明了 SetStaticField例程名及結果類型。應將SetStaticField中的type替換爲域的Java類型(或使用表中的某個 實際例程名),然後將NativeType替換爲該例程對應的本地類型。

 *SetStaticField例程名* 本地類型 
 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:如果系統內存不足。

CallStaticMethod,CallStaticMethodA,CallStaticMethodV 例程

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派生,而不能從其超類派生。

CallStaticMethod例程

編程人員應將要傳給方法的所有參數緊跟着放在methodID參數之後。CallStaticMethod routine接受這些參數並將其傳給 編程人員所要調用的Java方法。

CallStaticMethodA例程

編程人員應將要傳給方法的所有參數放在緊跟在methodID參數之後的jvalues類型數組args中。CallStaticMethodA routine接受 這些數組中的參數並將其傳給編程人員所要調用的Java方法。

CallStaticMethodV例程

編程人員應將要傳給方法的所有參數放在緊跟在methodID參數之後的va_list類型參數args中。CallStaticMethodV routine接受 這些參數並將其傳給編程人員所要調用的Java方法。

下表根據結果類型說明了各個方法調用例程。用戶應將CallStaticMethod中的type替換爲所調用方法的Java類型(或使用 表中的實際方法調用例程名),同時將NativeType替換爲該例程相應的本地類型。

 *CallStaticMethod例程名* 本地類型 
 CallStaticVoidMethod() CallStaticVoidMethodA() CallStaticVoidMethodV() void 
 CallStaticObjectMethod() CallStaticObjectMethodA() CallStaticObjectMethodV() jobject 
 CallStaticBooleanMethod() CallStaticBooleanMethodA() CallStaticBooleanMethodV() jboolean 
 CallStaticByteMethod() CallStaticByteMethodA() CallStaticByteMethodV() jbyte 
 CallStaticCharMethod() CallStaticCharMethodA() CallStaticCharMethodV() jchar 
 CallStaticShortMethod() CallStaticShortMethodA() CallStaticShortMethodV() jshort 
 CallStaticIntMethod() CallStaticIntMethodA() CallStaticIntMethodV() jint 
 CallStaticLongMethod() CallStaticLongMethodA() CallStaticLongMethodV() jlong 
 CallStaticFloatMethod() CallStaticFloatMethodA() CallStaticFloatMethodV() jfloat 
 CallStaticDoubleMethod() CallStaticDoubleMethodA() CallStaticDoubleMethodV() jdouble 

參數: env:JNI接口指針。 clazz:Java類對象。 methodID:靜態方法ID。

CallStaticMethod例程的其它參數: 要傳給靜態方法的參數。

CallStaticMethodA例程的其它參數: args:參數數組。

CallStaticMethodV例程的其它參數: args:參數的va_list。

返回值: 返回調用靜態Java方法的結果。

拋出: 執行Java方法時拋出的異常。

字符串操作

NewString 利用Unicode字符數組構造新的java.lang.String對象。

jstring NewString(JNIEnv *env, const jchar *unicodeChars, jsize len);

參數: env:JNI接口指針。 unicodeChars:指向Unicode字符串的指針。 len:Unicode字符串的長度。

返回值: Java字符串對象。如果無法構造該字符串,則爲NULL。

拋出: OutOfMemoryError:如果系統內存不足。

GetStringLength 返回Java字符串的長度(Unicode字符數)。

jsize GetStringLength(JNIEnv *env, jstring string);

參數: env:JNI接口指針。 string:Java字符串對象。

返回值: Java 字符串的長度。

GetStringChars 返回指向字符串的Unicode字符數組的指針。該指針在調用ReleaseStringchars()前一直有效。如果isCopy非空,則在複製完成後將*isCopy設爲JNI_TRUE。如果沒有複製,則設爲JNI_FALSE。

const jchar * GetStringChars(JNIEnv *env, jstring string, jboolean *isCopy);

參數: env:JNI接口指針。 string:Java字符串對象。 isCopy:指向布爾值的指針。

返回值: 指向Unicode字符串的指針,如果操作失敗,則返回NULL。

ReleaseStringChars 通知虛擬機平臺相關代碼無需再訪問chars。參數chars是一個指針,可通過GetStringChars()從string獲得。

void ReleaseStringChars(JNIEnv *env, jstring string, const jchar *chars);

參數: env:JNI接口指針。 string:Java字符串對象。 chars:指向Unicode字符串的指針。

NewStringUTF 利用UTF-8字符數組構造新java.lang.String對象。

jstring NewStringUTF(JNIEnv *env, const char *bytes);

參數: env:JNI接口指針。如果無法構造該字符串,則爲NULL。 bytes:指向UTF-8字符串的指針。

返回值: Java字符串對象。如果無法構造該字符串,則爲NULL。

拋出: OutOfMemoryError:如果系統內存不足。

GetStringUTFLength 以字節爲單位返回字符串的UTF-8長度。

jsize GetStringUTFLength(JNIEnv *env, jstring string);

參數: env:JNI接口指針。 string:Java字符串對象。

返回值: 返回字符串的UTF-8長度。

GetStringUTFChars 返回指向字符串的UTF-8字符數組的指針。該數組在被ReleaseStringUTFChars()釋放前將一直有效。如果isCopy不是NULL,*isCopy在複製完成後即被設爲JNI_TRUE。如果未複製,則設爲JNI_FALSE。

const char* GetStringUTFChars(JNIEnv *env, jstring string, jboolean *isCopy);

參數: env:JNI接口指針。 string:Java字符串對象。 isCopy:指向布爾值的指針。

返回值: 指向UTF-8字符串的指針。如果操作失敗,則爲NULL。

ReleaseStringUTFChars 通知虛擬機平臺相關代碼無需再訪問utf。utf參數是一個指針,可利用GetStringUTFChars()從string獲得。

void ReleaseStringUTFChars(JNIEnv *env, jstring string, const char *utf);

參數: env:JNI接口指針。 string:Java字符串對象。 utf:指向UTF-8字符串的指針。

數組操作

GetArrayLength 返回數組中的元素數。

jsize GetArrayLength(JNIEnv *env, jarray array);

參數: env:JNI接口指針。 array:Java數組對象。

返回值: 數組的長度。

NewObjectArray 構造新的數組,它將保存類elementClass中的對象。所有元素初始值均設爲initialElement。

jarray NewObjectArray(JNIEnv *env, jsize length, jclass elementClass, jobject initialElement);

參數: env:JNI接口指針。 length:數組大小。 elementClass:數組元素類。 initialElement:初始值。

返回值: Java數組對象。如果無法構造數組,則爲NULL。

拋出: OutOfMemoryError:如果系統內存不足。

GetObjectArrayElement 返回Object數組的元素。

jobject GetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index);

參數: env:JNI接口指針。 array:Java數組。 index:數組下標。

返回值: Java對象。

拋出: ArrayIndexOutOfBoundsException:如果index不是數組中的有效下標。

SetObjectArrayElement 設置Object數組的元素。

void SetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index, jobject value);

參數: env:JNI接口指針。 array:Java數組。 index:數組下標。 value:新值。

拋出: ArrayIndexOutOfBoundsException:如果index不是數組中的有效下標。 ArrayStoreException:如果value的類不是數組元素類的子類。

NewArray例程

ArrayType New<PrimitiveType>Array(JNIEnv *env, jsize length);

用於構造新基本類型數組對象的一系列操作。下表說明了特定的基本類型數組構造函數。用戶應把NewArray替換爲 某個實際的基本類型數組構造函數例程名(見下表),然後將ArrayType替換爲該例程相應的數組類型。

 *NewArray例程* 數組類型 
 NewBooleanArray() jbooleanArray 
 NewByteArray() jbyteArray 
 NewCharArray() jcharArray 
 NewShortArray() jshortArray 
 NewIntArray() jintArray 
 NewLongArray() jlongArray 
 NewFloatArray() jfloatArray 
 NewDoubleArray() jdoubleArray 

參數: env:JNI接口指針。 length:數組長度。

返回值: Java數組。如果無法構造該數組,則爲NULL。

GetArrayElements例程

NativeType *Get<PrimitiveType>ArrayElements(JNIEnv *env, ArrayType array, jboolean *isCopy);

一組返回基本類型數組體的函數。結果在調用相應的ReleaseArrayElements()函數前將一直有效。由於返回的數組 可能是Java數組的副本,因此對返回數組的更改不必在基本類型數組中反映出來,直到調用了 ReleaseArrayElements()。如果isCopy不是NULL,*isCopy在複製完成後即被設爲JNI_TRUE。如果未複製,則設爲JNI_FALSE。

下表說明了特定的基本類型數組元素訪問器。應進行下列替換;

  1. 將GetArrayElements替換爲表中某個實際的基本類型元素訪問器例程名。
  2. 將ArrayType替換爲對應的數組類型。
  3. 將NativeType替換爲該例程對應的本地類型。

不管布爾數組在Java虛擬機中如何表示,GetBooleanArrayElements()將始終返回一個jbooleans類型的指針,其中每一字節代表一個 元素(開包表示)。內存中將確保所有其它類型的數組爲連續的。

 *GetArrayElements例程* 數組類型 本地類型 
 GetBooleanArrayElements() jbooleanArray jboolean 
 GetByteArrayElements() jbyteArray jbyte 
 GetCharArrayElements() jcharArray jchar 
 GetShortArrayElements() jshortArray jshort 
 GetIntArrayElements() jintArray jint 
 GetLongArrayElements() jlongArray jlong 
 GetFloatArrayElements() jfloatArray jfloat 
 GetDoubleArrayElements() jdoubleArray jdouble 

參數: env:JNI接口指針。 array:Java字符串對象。 isCopy:指向布爾值的指針。

返回值: 返回指向數組元素的指針,如果操作失敗,則爲NULL。

ReleaseArrayElements例程

void Release<PrimitiveType>ArrayElements(JNIEnv *env, ArrayType array, NativeType *elems, jint mode);

通知虛擬機平臺相關代碼無需再訪問elems的一組函數。elems參數是一個通過使用對應的GetArrayElements()函數 由array導出的指針。必要時,該函數將把對elems的修改複製回基本類型數組。mode參數將提供有關如何釋放數組緩衝區的信息。 如果elems不是array中數組元素的副本,mode將無效。否則,mode將具有下表所述的功能:

 基本類型數組釋放模 動作 
 0 複製回內容並釋放elems緩衝區 
 JNI_COMMIT 複製回內容但不釋放elems緩衝區 
 JNI_ABORT 釋放緩衝區但不復制回變化 

多數情況下,編程人員將把“0”傳給mode參數以確保固定的數組和複製的數組保持一致。其它選項可以使編程人員進一步控制內存 管理,但使用時務必慎重。下表說明了構成基本類型數組撤消程序系列的特定例程。應進行如下替換;

  1. 將ReleaseArrayElements 替換爲下表中的某個實際基本類型數組撤消程序例程名。
  2. 將ArrayType替換爲對應的數組類型。
  3. 將NativeType替換爲該例程對應的本地類型。
 *ReleaseArrayElements例程* 數組類型 本地類型 
 ReleaseBooleanArrayElements() jbooleanArray jboolean 
 ReleaseByteArrayElements() jbyteArray jbyte 
 ReleaseCharArrayElements() jcharArray jchar 
 ReleaseShortArrayElements() jshortArray jshort 
 ReleaseIntArrayElements() jintArray jint 
 ReleaseLongArrayElements() jlongArray jlong 
 ReleaseFloatArrayElements() jfloatArray jfloat 
 ReleaseDoubleArrayElements() jdoubleArray jdouble 

參數: env:JNI接口指針。 array:Java數組對象。 elems:指向數組元素的指針。 mode:釋放模式。

GetArrayRegion例程

void Get<PrimitiveType>ArrayRegion(JNIEnv *env, ArrayType array, jsize start, jsize len, NativeType *buf);

將基本類型數組某一區域複製到緩衝區中的一組函數。 下表說明了特定的基本類型數組元素訪問器。應進行如下替換:

  1. 將GetArrayRegion替換爲表中的某個實際基本類型元素訪問器例程名。
  2. 將 ArrayType 替換爲對應的數組類型。
  3. 將 NativeType 替換爲該例程對應的本地類型。
 *GetArrayRegion例程* 數組類型 本地類型 
 GetBooleanArrayRegion() jbooleanArray jboolean 
 GetByteArrayRegion() jbyteArray jbyte 
 GetCharArrayRegion() jcharArray jchar 
 GetShortArrayRegion() jshortArray jshort 
 GetIntArrayRegion() jintArray jint 
 GetLongArrayRegion() jlongArray jlong 
 GetFloatArrayRegion() jfloatArray jfloat 
 GetDoubleArrayRegion() jdoubleArray jdouble 

參數: env:JNI接口指針。 array:Java指針。 start:起始下標。 len:要複製的元素數。 buf:目的緩衝區。

拋出: ArrayIndexOutOfBoundsException:如果區域中的某個下標無效。

SetArrayRegion例程

void Set<PrimitiveType>ArrayRegion(JNIEnv *env, ArrayType array, jsize start, jsize len, NativeType *buf);

將基本類型數組的某一區域從緩衝區中複製回來的一組函數。下表說明了特定的基本類型數組元素訪問器。應進行如下替換:

  1. 將SetArrayRegion替換爲表中的實際基本類型元素訪問器例程名。
  2. 將ArrayType替換爲對應的數組類型。
  3. 將NativeType替換爲該例程對應的本地類型。
 *SetArrayRegion例程* 數組類型 本地類型 
 SetBooleanArrayRegion() jbooleanArray jboolean 
 SetByteArrayRegion() jbyteArray jbyte 
 SetCharArrayRegion() jcharArray jchar 
 SetShortArrayRegion() jshortArray jshort 
 SetIntArrayRegion() jintArray jint 
 SetLongArrayRegion() jlongArray jlong 
 SetFloatArrayRegion() jfloatArray jfloat 
 SetDoubleArrayRegion() jdoubleArray jdouble 

參數: env:JNI接口指針。 array: Java數組。 start:起始下標。 len:要複製的元素數。 buf:源緩衝區。

拋出: ArrayIndexOutOfBoundsException:如果區域中的某個下標無效。

註冊本地方法

RegisterNatives

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:如果找不到指定的方法或方法不是本地方法。

UnregisterNatives

jint UnregisterNatives(JNIEnv *env, jclass clazz);

取消註冊類的本地方法。類將返回到鏈接或註冊了本地方法函數前的狀態。該函數不應在常規平臺相關代碼中使用。 相反,它可以爲某些程序提供一種重新加載和重新鏈接本地庫的途徑。

參數: env:JNI接口指針。 clazz:Java類對象。

返回值: 成功時返回“0”;失敗時返回負數。

監視程序操作

MonitorEnter

jint MonitorEnter(JNIEnv *env, jobject obj);

進入與obj所引用的基本Java對象相關聯的監視程序。每個Java對象都有一個相關聯的監視程序。如果當前線程已經擁有與obj相 關聯的監視程序,它將使指示該線程進入監視程序次數的監視程序計數器增 1。如果與 obj 相關聯的監視程序並非由某個線程所 擁有,則當前線程將變爲該監視程序的所有者,同時將該監視程序的計數器設置爲 1。如果另一個線程已擁有與 obj 關聯的監視 程序,則在監視程序被釋放前當前線程將處於等待狀態。監視程序被釋放後,當前線程將嘗試重新獲得所有權。

參數: env:JNI接口指針。 obj:常規Java對象或類對象。

返回值: 成功時返回“0”;失敗時返回負數。

MonitorExit

jint MonitorExit(JNIEnv *env, jobject obj);

當前線程必須是與obj所引用的基本Java對象相關聯的監視程序的所有者。線程將使指示進入監視程序次數的計數器減 1。 如果計數器的值變爲 0,當前線程釋放監視程序。

參數: env:JNI接口指針。 obj:常規Java對象或類對象。

返回值: 成功時返回“0”;失敗時返回負數。

Java虛擬機接口

GetJavaVM

jint GetJavaVM(JNIEnv *env, JavaVM **vm);

返回與當前線程相關聯的Java虛擬機接口(用於調用API中)。結果將放在第二個參數vm所指向的位置。

參數: env:JNI接口指針。 vm:指向放置結果的位置的指針。

返回值: 成功時返回“0”;失敗時返回負數。


調用API

調用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;

調用API函數

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結構,就可以使用這些函數。

JNI_GetDefaultJavaVMInitArgs

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”;如果所請求的版本未得到支持,則返回負數。

JNI_GetCreatedJavaVMs

jint JNI_GetCreatedJavaVMs(JavaVM **vmBuf, jsize bufLen, jsize *nVMs);

返回所有已創建的Java虛擬機。將指向虛擬機的指針依據其創建順序寫入vmBuf緩衝區。最多寫入bufLen項。在*nVMs中返回所創建 虛擬機的總數。JDK 1.1不支持在單個進程中創建多個虛擬機。

參數: vmBuf:指向將放置虛擬機結構的緩衝區的指針。 bufLen:緩衝區的長度。 nVMs:指向整數的指針。

返回值: 成功時返回“0”;失敗則返回負數。

JNI_CreateJavaVM

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”;失敗則返回負數。

DestroyJavaVM

jint DestroyJavaVM(JavaVM *vm);

卸載Java虛擬機並回收資源。只有主線程能夠卸載虛擬機。調用DestroyJavaVM() 時,主線程必須是唯一的剩餘用戶線程。

參數: vm:將銷燬的Java虛擬機。

返回值: 成功時返回“0”;失敗則返回負數。

JDK 1.1.2 不支持卸載虛擬機。

AttachCurrentThread

jint AttachCurrentThread(JavaVM *vm, JNIEnv **p_env, void *thr_args);

將當前線程連接到Java虛擬機。在JNIEnv參數中返回JNI接口指針。試圖連接已經連接的線程將不執行任何操作。本地線程不能同時 連接到兩個Java虛擬機上。

參數: vm:當前線程所要連接到的虛擬機。 p_env:指向位置(其中放置當前線程的 JNI 接口指針)的指針。 thr_args:特定於虛擬機的線程連接參數。

返回值: 成功時返回“0”;失敗則返回負數。

DetachCurrentThread

jint DetachCurrentThread(JavaVM *vm);

斷開當前線程與Java虛擬機之間的連接。釋放該線程佔用的所有Java監視程序。通知所有等待該線程終止的Java線程。主線程 (即創建Java虛擬機的線程)不能斷開與虛擬機之間的連接。作爲替代,主線程必須調用JNI_DestroyJavaVM()來卸載整個虛擬機。

參數: vm:當前線程將斷開連接的虛擬機。

返回值: 成功時返回“0”;失敗則返回負數。

CreateFile

(1)函數原型

HANDLE CreateFile(
	LPCTSTR lpfileName
	DWORD deDesiredAccess,
	DWORD dwShareMode,
	LPSECURITY_ATTRIBUTES lpSecurityAttributes
	DWORD dwCreationDesposition,
	DWORD dwFlagsAndAtrributes,
	HANDLE hTemplateFile
);

(2)函數說明

該函數創建、打開或截斷一個文件,並返回一個能夠被用來存取該文件的句柄。此句柄允許讀書據、寫數據以及移動文件的指針。 CreateFile函數既可以做爲一個寬自負函數使用,也可以作爲一個ANSI函數來用。

(3)參數說明

lpFileName:指向文件字符串的指針。 dwDesireAccess:制定文件的存取模式,可以取下列值: 0:制定可以查詢對象。 GENERIC_READ:指定可以從文件中度去數據。 GENERIC_WRITE:指定可以向文件中寫數據。 dwShareMode:指定文件的共享模式,可以取下列值:

  1. 0:不共享。
  2. FILE_SHARE_DELETE:在 Windows NT 系統中,只有爲了刪除文件而進行的打開操作纔會成功。
  3. FILE_SHARE_READ:只有爲了從文件中度去數據而進行的打開操作纔會成功。
  4. FILE_SHARE_WRITE:只有爲了向文件中寫數據而進行的打開操作纔會成功。

lpSecurityAttributes:指定文件的安全屬性。 dwCreationDisopsition:指定創建文件的方式,可以取以下值:

  1. CREATE_NEW:創建新文件,如果文件已存在,則函數失敗。
  2. CREATE_ALWAYS:創建愛內心文件,如果文件已存在,則函數將覆蓋並清除舊文件。
  3. OPEN_EXISTING:打開文件,如果文件不存在,函數將失敗。
  4. OPEN_ALWAYS:打開文件,如果文件不存在,則函數將創建一個新文件。
  5. TRUNCATE_EXISTING:打開外呢間,如果文件存在,函數將文件的大小設爲零,如果文件不存在,函數將失敗返回。

dwFlagsAndAtrributes:指定新建文件的屬性和標誌,它可以取以下值: 1. FILE_ATTRIBUTE_ARCHIVE:歸檔屬性。 2. FILE_ATTRIBUTE_HIDDEN:隱藏屬性。 3. FILE_ATTRIBUTE_NORMAL:正常屬性。 4. FILE_ATTRIBUTE_READONLY:只讀屬性。 5. FILE_ATTRIBUTE_SYSTEM:系統文件。 6. FILE_ATTRIBUTE_TEMPORARY:臨時存儲文件,系統總是將臨時文件的所有數據讀入內存中,以加速對該文件的訪問速度。 用戶應該儘快刪除不再使用的臨時文件。 7. FILE_FLAG_OVERLAPPED:用戶可以通過一個 OVERLAPPED 結構變量來保存和設置文件讀寫指針,從而可以完成重疊式的讀和寫。 一般用於數量比較大的讀些操作。

hTemplateFile:指向一個具有GENERIC_READ屬性的文件的句柄,該文件爲要創建的文件提供文件屬性和文件擴展屬性。

(4)注意事項

函數成功將返回指定文件的句柄,否則返回NULL。

(5)典型示例:

...
	char szFile[64];
	HANDLE handle;
	unsigned long lWritten,lRead;
	
	handle = CreateFile("c:\\windows\\desktop\\example.txt",GENERIC_READ|GENERIC_W
						RITE,FILE_SHARE_READ,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,0);
	
	if(handle==INVALID_HANDLE_VALUE){
		MessageBox(NULL,"Error Create File!","Error",MB_OK);
		break;
	}else
		MessageBox(NULL,"Open file Success!","Open file",MB_OK);

總結

至此整個JNI完全手冊引用結束。日後開發過程中該手冊相當於一本字典,可以用來查閱。


本文轉自:http://yanbober.github.io/2015/02/14/android_studio_jni_1/


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章