《Java 本地接口規範》- 調用 API
調用 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
jintJNI_GetDefaultJavaVMInitArgs(void *vm_args);
返回 Java 虛擬機的缺省配置。在調用該函數之前,平台相關代碼必須將 vm_args->version
域設置為它所期望虛擬機支持的 JNI 版本。在 JDK 1.1.2 中,必須將
vm_args->version
設置為 0x00010001
。(JDK1.1 不要求平台相關代碼設置版本域。為了向後兼容性,如果沒有設置版本域,則 JDK 1.1.2 假定所請求的版本為 0x00010001。JDK 的未來版本將要求把版本域設置為適當的值。) 該函數返回後,將把vm_args->version
設置為虛擬機支持的實際 JNI 版本。
參數:
vm_args:指向 VM-specific initialization
(特定於虛擬機的初始化)結構的指針,缺省參數填入該結構。
返回值:
如果所請求的版本得到支持,則返回“0”;如果所請求的版本未得到支持,則返回負數。
JNI_GetCreatedJavaVMs
jintJNI_GetCreatedJavaVMs(JavaVM **vmBuf, jsize bufLen,
jsize *nVMs);
返回所有已創建的Java 虛擬機。將指向虛擬機的指針依據其創建順序寫入 vmBuf 緩衝區。最多寫入 bufLen 項。在 *nVMs 中返回所創建虛擬機的總數。
參數:
vmBuf:指向將放置虛擬機結構的緩衝區的指針。
返回值:
成功時返回“0”;失敗則返回負數。
JNI_CreateJavaVM
jintJNI_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
jintDestroyJavaVM(JavaVM *vm);
卸載 Java 虛擬機並回收資源。隻有主線程能夠卸載虛擬機。調用 DestroyJavaVM()
時,主線程必須是唯一的剩餘用戶線程。
參數:
vm:將銷毀的 Java 虛擬機。
返回值:
成功時返回“0”;失敗則返回負數。
AttachCurrentThread
jintAttachCurrentThread(JavaVM *vm, JNIEnv **p_env,
void *thr_args);
將當前線程連接到 Java 虛擬機。在 JNIEnv
參數中返回 JNI 接口指針。
參數:
vm:當前線程所要連接到的虛擬機。
p_env
:指向位置(其中放置當前線程的 JNI 接口指針)的指針。
thr_args
:特定於虛擬機的線程連接參數。
返回值:
成功時返回“0”;失敗則返回負數。
DetachCurrentThread
jintDetachCurrentThread(JavaVM *vm);
斷開當前線程與 Java 虛擬機之間的連接。釋放該線程占用的所有 Java 監視程序。通知所有等待該線程終止的 Java 線程。
主線程(即創建 Java 虛擬機的線程)不能斷開與虛擬機之間的連接。作為替代,主線程必須調用 JNI_DestroyJavaVM()
來卸載整個虛擬機。
參數:
vm:當前線程將斷開連接的虛擬機。
返回值:
成功時返回“0”;失敗則返回負數。
最後更新:2017-04-02 06:52:01