JAVA以其跨平台的特性深受人們喜愛,而又正由於它的跨平台的目的,使得它和本地機器的各種內部聯絡變得很少,約束了它的功能。解決JAVA對本地操作的一種方法就是JNI。
JAVA通過JNI調用本地方法,而本地方法是以庫檔案的形式存放的(在WINDOWS平台上是DLL檔案形式,在UNIX機器上是SO檔案形式)。通過調用本地的庫檔案的內部方法,使JAVA可以實現和本地機器的緊密聯絡,調用系統級的各介面方法。
簡單介紹及應用如下:
一、JAVA中所需要做的工作
在JAVA程式中,首先需要在類中聲明所調用的庫名稱,如下:
static {
System.loadLibrary(“testdll”);
}
在這裡,庫的副檔名字可以不用寫出來,究竟是DLL還是SO,由系統自己判斷。
還需要對將要調用的方法做本地聲明,關鍵字為native。並且只需要聲明,而不需要具體實現。如下:
public native static void set(int i);
public native static int get();
然後編譯該JAVA程式檔案,產生CLASS,再用JAVAH命令,JNI就會產生C/C++的標頭檔。
例如程式testdll.java,內容為:
public class testdll
{
static
{
System.loadLibrary("testdll");
}
public native static int get();
public native static void set(int i);
public static void main(String[] args)
{
testdll test = new testdll();
test.set(10);
System.out.println(test.get());
}
}
用javac testdll.java編譯它,會產生testdll.class。
再用javah testdll,則會在目前的目錄下產生testdll.h檔案,這個檔案需要被C/C++程式調用來產生所需的庫檔案。
二、C/C++中所需要做的工作
對於已產生的.h標頭檔,C/C++所需要做的,就是把它的各個方法具體的實現。然後編譯串連成庫檔案即可。再把庫檔案拷貝到JAVA程式的路徑下面,就可以用JAVA調用C/C++所實現的功能了。
接上例子。我們先看一下testdll.h檔案的內容:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class testdll */
#ifndef _Included_testdll
#define _Included_testdll
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: testdll
* Method: get
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_testdll_get
(JNIEnv *, jclass);
/*
* Class: testdll
* Method: set
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_testdll_set
(JNIEnv *, jclass, jint);
#ifdef __cplusplus
}
#endif
#endif
在具體實現的時候,我們只關心兩個函數原型
JNIEXPORT jint JNICALL Java_testdll_get (JNIEnv *, jclass);
和
JNIEXPORT void JNICALL Java_testdll_set (JNIEnv *, jclass, jint);
這裡JNIEXPORT和JNICALL都是JNI的關鍵字,表示此函數是要被JNI調用的。而jint是以JNI為中介使JAVA的int類型與本地的int溝通的一種類型,我們可以視而不見,就當做int使用。函數的名稱是JAVA_再加上java程式的package路徑再加函數名組成的。參數中,我們也只需要關心在JAVA程式中存在的參數,至於JNIEnv*和jclass我們一般沒有必要去碰它。
好,下面我們用testdll.cpp檔案具體實現這兩個函數:
#include "testdll.h"
int i = 0;
JNIEXPORT jint JNICALL Java_testdll_get (JNIEnv *, jclass)
{
return i;
}
JNIEXPORT void JNICALL Java_testdll_set (JNIEnv *, jclass, jint j)
{
i = j;
}
編譯串連成庫檔案,本例是在WINDOWS下做的,產生的是DLL檔案。並且名稱要與JAVA中需要調用的一致,這裡就是testdll.dll
對於在VC6.0中,可以開啟VC++6.0,建立->; 工程 ->; win32 Dynamic-Link Library .在嚮導中選擇空工程。
把剛才javah產生的那個標頭檔加入工程 .然後 檔案->;建立->;textFile 自己做一個
.cpp檔案.實現自己的native函數.
對於在visual stdio 2005中,可以建立一個WIN32的空項目,下一步中選擇DLL和空項目。然後把上面的testdll.h和testdll.cpp加進去,編譯就可以產生testdll.dll檔案
通常情況下,VC的編譯器會找不到jni.h和jni_md.h標頭檔,這兩個標頭檔可以在JDK目錄下的include下找到。vc6.0也有可能找不到cl.exe這個編譯器,可以上網下載後再設定環境變數就可以了。通常這個編譯器會在VC目錄下的vc/bin目錄下可以找到的。
把testdll.dll拷貝到testdll.class的目錄下,java testdll運行它,就可以觀察到結果了