編輯:關於android開發
該篇文章完全引用自《JNI完全手冊》完整版,用來方便查詢查閱,同時作為該系列教程的基礎知識。感謝原文檔作者。
文檔所依賴的版本是比較低的,但是恰恰是低版本才能更容易上手學習。文檔也有些枯燥,適合開發中參考查詢和粗略概況性
的浏覽掌握大局使用,也是下來幾篇的基礎性指導文檔。下來幾篇不會再解釋代碼簡單函數釋義,只會說重點,遇到不懂的來
這篇文章搜索函數名即可查閱函數詳情。
平台相關代碼是通過調用JNI函數來訪問Java虛擬機功能的。JNI函數可通過接口指針來獲得。接口指針是指針的指針,它指向
一個指針數組,而指針數組中的每個元素又指向一個接口函數。每個接口函數都處在數組的某個預定偏移量中。下圖說明了接
口指針的組織結構。
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方法)並不放在本地庫中。下例中,不必用長名來鏈接本地方法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對象由引用來傳遞。虛擬機必須跟蹤傳到平台相關代碼中的對象,以使這些對象不會被垃圾收集器釋放。反之,平台相關代碼必須能用某種方式通知虛擬機它不再需要那些對象,同時,垃圾收集器必須能夠移走被平台相關代碼引用過的對象。
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,則它必須確保:保留對所涉及類的活引用,或重新計算該方法ID或域ID。JNI對域ID和方法ID的內部實現並不施加任何限制。
JNI不檢查諸如傳遞NULL指針或非法參數類型之類的編程錯誤。非法的參數類型包括諸如要用Java類對象時卻用了普通Java對象這樣的錯誤。JNI不檢查這些編程錯誤的理由如下:
大多數C庫函數對編程錯誤不進行防范。例如,printf()函數在接到一個無效地址時通常是引起運行錯而不是返回錯誤代碼。強迫C庫函數檢查所有可能的錯誤情況將有可能引起這種檢查被重復進行–先是在用戶代碼中進行,然後又在庫函數中再次進行。
程序員不得將非法指針或錯誤類型的參數傳遞給JNI函數。否則,可能產生意想不到的後果,包括可能使系統狀態受損或使虛擬機崩潰。
JNI允許本地方法拋出任何Java異常。本地方法也可以處理突出的Java異常。未被處理的Java異常將被傳回虛擬機中。
一些JNI函數使用Java異常機制來報告錯誤情況。大多數情況下,JNI函數通過返回錯誤代碼並拋出Java異常來報告錯誤情況。錯誤代碼通常是特殊的返回值(如 NULL),這種特殊的返回值在正常返回值范圍之外。因此,程序員可以:快速檢查上一個JNI調用所返回的值以確定是否出錯,並通過調用函數ExceptionOccurred()來獲得異常對象,它含有對錯誤情況的更詳細說明。
在以下兩種情況中,程序員需要先查出異常,然後才能檢查錯誤代碼:
在所有其它情況下,返回值如果不是錯誤代碼值就可確保沒有拋出異常。
在多個線程的情況下,當前線程以外的其它線程可能會拋出異步異常。異步異常並不立即影響當前線程中平台相關代碼的執行,直到出現下列情況:該平台相關代碼調用某個有可能拋出同步異常的JNI函數,或者該平台相關代碼用 ExceptionOccurred() 顯式檢查同步異常或異步異常。
注意,只有那些有可能拋出同步異常的JNI函數才檢查異步異常。本地方法應在必要的地方(例如,在一個沒有其它異常檢查的緊密循環中)插入ExceptionOccurred() 檢查以確保當前線程可在適當時間內對異步異常作出響應。
可用兩種方法來處理平台相關代碼中的異常:
拋出了某個異常之後,平台相關代碼必須先清除異常,然後才能進行其它的JNI調用。當有待定異常時,只有以下這些JNI函數可被安全地調用:ExceptionOccurred()、ExceptionDescribe()和ExceptionClear()。ExceptionDescribe()函數將打印有關待定異常的調試消息。
本章討論JNI如何將Java類型映射到本地C類型。
基本類型和本地等效類型表:
為了使用方便,特提供以下定義:
#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是常規的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方法:
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范圍內的字符都用單字節表示,如下所示:
|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函數傳遞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, };
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);
參數:
env:JNI接口指針。
name:類全名(即包名後跟類名,之間由“/”分隔)。如果該名稱以“[”(數組簽名字符)打頭,則返回一個數組類。
返回值:
返回類對象全名。如果找不到該類,則返回NULL。
拋出:
ClassFormatError:如果類數據指定的類無效。
ClassCircularityError:如果類或接口是自身的超類或超接口。
NoClassDefFoundError:如果找不到所請求的類或接口的定義。
OutOfMemoryError:如果系統內存不足。
jclass GetSuperclass(JNIEnv *env, jclass clazz);
參數:
env:JNI接口指針。
clazz:Java類對象。
返回值:
由clazz所代表的類的超類或NULL。
jboolean IsAssignableFrom(JNIEnv *env, jclass clazz1, jclass clazz2);
參數:
env:JNI接口指針。
clazz1:第一個類參數。
clazz2:第二個類參數。
返回值:
下列某個情況為真時返回JNI_TRUE:
第一及第二個類參數引用同一個Java類。
第一個類是第二個類的子類。
第二個類是第一個類的某個接口。
jint Throw(JNIEnv *env, jthrowable obj);
參數:
env:JNI接口指針。
obj:java.lang.Throwable對象。
返回值:
成功時返回0,失敗時返回負數。
拋出:
java.lang.Throwable對象obj。
jint ThrowNew(JNIEnv *env, jclass clazz, const char *message);
參數:
env:JNI接口指針。
clazz:java.lang.Throwable的子類。
message:用於構造java.lang.Throwable對象的消息。
返回值:
成功時返回0,失敗時返回負數。
拋出:
新構造的java.lang.Throwable對象。
jthrowable ExceptionOccurred(JNIEnv *env);
參數:
env:JNI接口指針。
返回值:
返回正被拋出的異常對象,如果當前無異常被拋出,則返回NULL。
void ExceptionDescribe(JNIEnv *env);
參數:
env:JNI接口指針。
void ExceptionClear(JNIEnv *env);
參數:
env:JNI接口指針。
void FatalError(JNIEnv *env, const char *msg);
參數:
env:JNI接口指針。
msg:錯誤消息。
jobject NewGlobalRef(JNIEnv *env, jobject obj);
參數:
env:JNI接口指針。
obj:全局或局部引用。
返回值:
返回全局引用。如果系統內存不足則返回NULL。
void DeleteGlobalRef(JNIEnv *env, jobject globalRef);
參數:
env:JNI接口指針。
globalRef:全局引用。
void DeleteLocalRef(JNIEnv *env, jobject localRef);
參數:
env:JNI接口指針。
localRef:局部引用。
jobject AllocObject(JNIEnv *env, jclass 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);
方法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。
NewObject 的其它參數:
傳給構造函數的參數。
NewObjectA的其它參數:
args:傳給構造函數的參數數組。
NewObjectV的其它參數:
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);
參數:
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替換為該例程對應的本地類型。
參數:
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替換為該例程對應的本地類型。
參數:
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替換為該例程相應的本地類型。
參數:
env:JNI接口指針。
obj:Java對象。
methodID:方法ID。
Call[type]Method例程的其它參數:
要傳給Java方法的參數。
Call[type]MethodA例程的其它參數:
args:參數數組。
Call[type]MethodV例程的其它參數:
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替換為該例程相應的本地類型。
參數:
env:JNI接口指針。
clazz:Java類。
obj: Java對象。
methodID:方法ID。
CallNonvirtual[type]Method例程的其它參數:
要傳給Java方法的參數。
CallNonvirtual[type]MethodA例程的其它參數:
args:參數數組。
CallNonvirtual[type]MethodV例程的其它參數:
args:參數的va_list。
返回值:
調用Java方法的結果。
拋出:
執行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替換為該例程對應的本地類型。
參數:
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替換為該例程對應的本地類型。
參數:
env:JNI接口指針。
clazz:Java類對象。
fieldID:靜態域ID。
value:域的新值。
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:如果系統內存不足。
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替換為該例程相應的本地類型。
參數:
env:JNI接口指針。
clazz:Java類對象。
methodID:靜態方法ID。
CallStatic[type]Method例程的其它參數:
要傳給靜態方法的參數。
CallStatic[type]MethodA例程的其它參數:
args:參數數組。
CallStatic[type]MethodV例程的其它參數:
args:參數的va_list。
返回值:
返回調用靜態Java方法的結果。
拋出:
執行Java方法時拋出的異常。
jstring NewString(JNIEnv *env, const jchar *unicodeChars, jsize len);
參數:
env:JNI接口指針。
unicodeChars:指向Unicode字符串的指針。
len:Unicode字符串的長度。
返回值:
Java字符串對象。如果無法構造該字符串,則為NULL。
拋出:
OutOfMemoryError:如果系統內存不足。
jsize GetStringLength(JNIEnv *env, jstring string);
參數:
env:JNI接口指針。
string:Java字符串對象。
返回值:
Java 字符串的長度。
const jchar * GetStringChars(JNIEnv *env, jstring string, jboolean *isCopy);
參數:
env:JNI接口指針。
string:Java字符串對象。
isCopy:指向布爾值的指針。
返回值:
指向Unicode字符串的指針,如果操作失敗,則返回NULL。
void ReleaseStringChars(JNIEnv *env, jstring string, const jchar *chars);
參數:
env:JNI接口指針。
string:Java字符串對象。
chars:指向Unicode字符串的指針。
jstring NewStringUTF(JNIEnv *env, const char *bytes);
參數:
env:JNI接口指針。如果無法構造該字符串,則為NULL。
bytes:指向UTF-8字符串的指針。
返回值:
Java字符串對象。如果無法構造該字符串,則為NULL。
拋出:
OutOfMemoryError:如果系統內存不足。
jsize GetStringUTFLength(JNIEnv *env, jstring string);
參數:
env:JNI接口指針。
string:Java字符串對象。
返回值:
返回字符串的UTF-8長度。
const char* GetStringUTFChars(JNIEnv *env, jstring string, jboolean *isCopy);
參數:
env:JNI接口指針。
string:Java字符串對象。
isCopy:指向布爾值的指針。
返回值:
指向UTF-8字符串的指針。如果操作失敗,則為NULL。
void ReleaseStringUTFChars(JNIEnv *env, jstring string, const char *utf);
參數:
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);
參數:
env:JNI接口指針。
length:數組大小。
elementClass:數組元素類。
initialElement:初始值。
返回值:
Java數組對象。如果無法構造數組,則為NULL。
拋出:
OutOfMemoryError:如果系統內存不足。
jobject GetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index);
參數:
env:JNI接口指針。
array:Java數組。
index:數組下標。
返回值:
Java對象。
拋出:
ArrayIndexOutOfBoundsException:如果index不是數組中的有效下標。
void SetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index, jobject value);
參數:
env:JNI接口指針。
array:Java數組。
index:數組下標。
value:新值。
拋出:
ArrayIndexOutOfBoundsException:如果index不是數組中的有效下標。
ArrayStoreException:如果value的類不是數組元素類的子類。
ArrayType New<PrimitiveType>Array(JNIEnv *env, jsize length);
用於構造新基本類型數組對象的一系列操作。下表說明了特定的基本類型數組構造函數。用戶應把New[PrimitiveType]Array替換為
某個實際的基本類型數組構造函數例程名(見下表),然後將ArrayType替換為該例程相應的數組類型。
參數:
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類型的指針,其中每一字節代表一個元素(開包表示)。內存中將確保所有其它類型的數組為連續的。
參數:
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將具有下表所述的功能:
多數情況下,編程人員將把“0”傳給mode參數以確保固定的數組和復制的數組保持一致。其它選項可以使編程人員進一步控制內存管理,但使用時務必慎重。下表說明了構成基本類型數組撤消程序系列的特定例程。應進行如下替換;
參數:
env:JNI接口指針。
array:Java數組對象。
elems:指向數組元素的指針。
mode:釋放模式。
void Get<PrimitiveType>ArrayRegion(JNIEnv *env, ArrayType array, jsize start, jsize len, NativeType *buf);
將基本類型數組某一區域復制到緩沖區中的一組函數。下表說明了特定的基本類型數組元素訪問器。應進行如下替換:
參數:
env:JNI接口指針。
array:Java指針。
start:起始下標。
len:要復制的元素數。
buf:目的緩沖區。
拋出:
ArrayIndexOutOfBoundsException:如果區域中的某個下標無效。
void Set<PrimitiveType>ArrayRegion(JNIEnv *env, ArrayType array, jsize start, jsize len, NativeType *buf);
將基本類型數組的某一區域從緩沖區中復制回來的一組函數。下表說明了特定的基本類型數組元素訪問器。應進行如下替換:
參數:
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”;失敗則返回負數。
(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:指定文件的共享模式,可以取下列值:
lpSecurityAttributes:指定文件的安全屬性。
dwCreationDisopsition:指定創建文件的方式,可以取以下值:
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完全手冊引用結束。日後開發過程中該手冊相當於一本字典,可以用來查閱。
感謝原中文版手冊作者,Admire,向大牛致敬,向開源致敬!
【工匠若水 http://blog.csdn.net/yanbober】 繼續閱讀《NDK-JNI實戰教程(三) 從一個比Hello World稍微復雜一丁點兒的例子說說模板》 http://blog.csdn.net/yanbober/article/details/45310589
來自: http://blog.csdn.net//yanbober/article/details/45310365
點擊文本改變改行背景色,彈出對話框,改行背景 我想單純靠一個文本實現微信回復評論的效果,在一列回復文本中點擊某一行,然後該行的背景色改變並且彈出對話框,如下圖,
Android 性能分析工具之 TraceView 使用說明 TraceView 是 Android 平台配備一個很好的性能分析的工具。它可以通過圖形化的方式讓我們了解我
Android--ListView下拉刷新,android--listview整理了下以前寫的小項目,ListView的下拉刷新,雖然小但還是想紀念下。。適合新手看,大神
Android WebView遠程代碼執行漏洞簡析 0x00 本文參考Android WebView 遠程代碼執行漏洞簡析。代碼地址為,https://github.