JNI是JDK的一部分,用於為Java提供一個本地代碼的接口。通過使用JNI編寫的程序能夠 確保你的代碼能夠完全的移植到所有的平台。JNI使得運行在JVM虛擬機上的Java代碼能夠操 作使用其它語言編寫的應用程序和庫,比如C/C++以及匯編語言等。此外JNI提供的某些API還 允許你把JVM嵌入到本地應用程序中。下圖表達了JNI所扮演的角色。
本文將通過一個 實例來闡述使用VC++6.0來實現JNI的完整過程。使用JNI來整合本地代碼和Java代碼的步驟是 確定的,沒有再創作的余地,所以讀者可以通過本文的步驟來逐步認識到,其實Java也是 "沒有什麼不可以"的。
一、JNI的實現任務描述:在Java中調用windows下 的消息框函數,並且從Java中傳遞一個字符串作為MessageBox函數的顯示文本參數,顯示在 消息框的中間。下面讓我們一起進入這一奇妙的旅程。
Step 1:寫一個Java類,在這 個類中包含了需要調用的本地方法的描述。
//WinMsgBox.java
package edu.netcom.jni;
public class WinMsgBox
{
static{
System.loadLibrary("WinMsgDll"); // (1)
}
public native void showMsgBox(String str); // (2)
}
(1)中WinMsgDll是 動態鏈接文件的文件名,不用加擴展名,因為在不同的平台下動態鏈接文件擴展名是不同的 ,由JVM自動識別,比如在Solaris下,會被轉換為WinMsgDll.so;而Win32環境下會轉換為 WinMsgDll.dll。這個文件名必須和Step 4中生成的文件名一致。這個文件的存放位置也很重 要,它只能被放在JVM屬性值java.library.path中指定的文件夾中。這個屬性值可以使用 System.getProperty("java.library.path");來查看。一般情況下,至少放在這 幾個位置是確定可靠的,windows安裝目錄下的system32下面,JDK安裝目錄下的bin下面,以 及調用主類文件的當前目錄。
(2)中指明了你必須用本地代碼實現的方法。
Step 2:提示符下使用命令javac -d . WinMsgBox.java編譯Step 1編寫的java文件 。
此時會在當前目錄下建立一個edu\netcom\jni目錄結構,並且一個WinMsgBox.class文件存在其中。
Step 3:提示符下使 用命令javah -jni edu.netcom.jni.WinMsgBox,此時會在當前目錄下產生一個 edu_netcom_jni_WinMsgBox.h文件,注意這個文件名是由(包名+類名)組成,中間用(_) 隔開。此文件內容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h> // (1)
/* Header for class edu_netcom_jni_WinMsgBox */
#ifndef _Included_edu_netcom_jni_WinMsgBox
#define _Included_edu_netcom_jni_WinMsgBox
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: edu_netcom_jni_WinMsgBox
* Method: showMsgBox
* Signature: (Ljava/lang/String;)V // (2)
*/
JNIEXPORT void JNICALL Java_edu_netcom_jni_WinMsgBox_showMsgBox
(JNIEnv *, jobject, jstring); // (3)
#ifdef __cplusplus
}
#endif
#endif
(1)包含的jni.h存在於JDK安裝目錄下的include下面 。
(2)(Ljava/lang/String;)V這是函數的標記符,當從本地方法端訪問Java端的方法 時,會用到這個標記符。JNI中為每種數據類型也定義了標記符,標記符的規則請查看JNI標 准文檔。
(3)在WinMsgBox.java中本地方法void showMsgBox(String str);的定義, 被映射為JNIEXPORT void JNICALL Java_edu_netcom_jni_WinMsgBox_showMsgBox(JNIEnv *, jobject, jstring); 其中函數名的映射規則是(Java_包名_類名_方法名),如果存在重載 的方法,則在後面還會增加每個參數的標記符。每一個方法映射到本地C函數後都會增加兩個 參數:JNIEnv *和jobject,關於這兩個參數的用法將在後面闡述。另外,所有Java中的數據 類型都會按一定規則進行映射為本地數據類型,這些數據類型都是在jni.h中定義的。下面分 別按照基本數據類型,和對象類型列出。
表1 Java基本類型到本地類型的映射
表2 Java中的類到本地類型的映射
Step 4:使用VC來編寫本地方法的實現函 數,最後編譯成.dll文件。過程如下:
1) 選擇new->projects(選擇Win32 Dynamic-Link Library,以Step 1中指定的庫名WinMsgDll作為工程名)->OK->An ampty DLL project->Finish。
2) 選擇Tools->Options->Directories(添 加目錄D:\J2SDK1.4.2_03\INCLUDE和D:\J2SDK1.4.2_03\INCLUDE\WIN32)。在這些目錄中包含JNI所 需的頭文件。
3) 將Step 3生成的edu_netcom_jni_WinMsgBox.h拷貝到WinMsgDll工程 文件夾中。然後FileView中添加這個頭文件。
4) 添加源文件WinMsgDll.cpp,內容如 下:
#include "windows.h"
#include "edu_netcom_jni_WinMsgBox.h"
/*
* Class: edu_netcom_jni_WinMsgBox
* Method: showMsgBox
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_edu_netcom_jni_WinMsgBox_showMsgBox
(JNIEnv * env, jobject obj, jstring str){
const char *msg;
msg = env->GetStringUTFChars (str,0);
MessageBox(NULL,msg,"Java invoke",MB_OK);
env->ReleaseStringUTFChars(str,msg);
}
5) 編譯生成 WinMsgBox.dll文件。並將這個.dll文件拷貝到Step 1中說明的目錄中。
注意:
1) 我們知道dll文件有兩種指明導出函數的方法,一種是在.def文件中定義,另一種 是在定義函數時使用關鍵字__declspec(dllexport)。而在JNI中函數定義中的關鍵字 JNIEXPORT實際在jni_md.h中如下定義,#define JNIEXPORT __declspec(dllexport),可見 JNI默認的導出函數使用第二種。使用第二種方式產生的導出函數名會根據編譯器發生變化, 在有的情況下會發生找不到導出函數的問題(我們在JSP中使用JNI時就發生了這種問題,百 思不得其解,後來強行加入一個.def文件就解決了)。因此最好是使用第一種方法自己定義 一個.def文件來指明導出函數,這種情況下會強制使用第一種方式產生導出函數。本例中可 以加入一個WinMsgDll.def文件,內容如下:
LIBRARY "WinMsgDll"
DESCRIPTION 'message Windows Dynamic Link Library'
EXPORTS
; Explicit exports can go here
Java_edu_netcom_jni_WinMsgBox_showMsgBox
2) 從本例中,我們可以看到 WinMsgBox.java決定了edu_netcom_jni_WinMsgBox.h,而後者又決定了WinMsgDll.dll,也就 是說,這是一個"牽一發而動全身"的過程,如果你改動了WinMsgBox.java,就一 定要把整個步驟都走一遍(這一點一定要切記,因為這也是我們跌得鼻青臉腫後才得出的警 世良言)。
3) 生成的.dll文件一定要正確拷貝到Step 1說明的目錄中,本例中是將 生成的WinMsgDll.dll和Step 5中的測試文件放在同一個目錄下的(這也是我們困惑了很久才 解決的問題)。
Step 5:編寫一個測試文件來測試對WinMsgDll.dll的調用。測試文 件TestJNI.java內容如下:
//TestJNI.java
import edu.netcom.jni.WinMsgBox;
public class TestJNI
{
public static void main(String[] args)
{
WinMsgBox box = new WinMsgBox();
box.showMsgBox("Wonderful!!");
}
}
編譯,運行,windows下的對話框躍然屏幕中間。到此為此,整個JNI的實 現過程就已經完成了。