Java通過JNI調用本地方法,而本地方法是以庫文件的形式存放的(在Windows平臺下是DLL文件形式,在UNIX機器上是SO文件形式)。通過調用本地的庫文件的內部方法,使Java可以實現和本地機器的緊密聯繫,調用系統級的各接口方法
使用的簡單流程:
一、Java中所需要做的工作
1、在Java程序中,首先需要在類中聲明所調用的庫名稱,如
- static {
- System.loadLibrary(“testdll”);
- }
這裏庫的擴展名可以不用寫出來,究竟是DLL還是SO,由系統自己判斷
2、還需要對將要調用的方法做本地聲明,關鍵字爲native。且只需要聲明,而不需要具體實現
- public native static void set(int i);
- public native static int get();
得到的Java程序如下:TestJNI.java
- public class TestJNI {
- 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());
- }
- }
再用javah TestJNI,則會在當前目錄下生成TestJNI.h文件
注意:用javah命令生成相應h頭文件的時候有可能會出現無法找到相應class文件的錯誤,建議使用如下格式在控制檯生成.h頭文件
D:\>javah -classpath . testdll
二、C/C++中所需要做的工作
1、打開VC++,新建工程->win32 DLL,在嚮導中選擇空工程。然後編寫testdll.cpp文件
- #include <windows.h>
- #include "TestJNI.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;
- }
- int WINAPI DllMain (HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved)
- {
- return TRUE ;
- }
說明:
(1)必須有DllMain函數,並且DllMain必須返回TRUE,否則系統將終止程序並彈出一個“啓動程序時出錯”對話框,其他兩個爲Java中調用的兩個函數的實現,解釋見最後的補充
(2)把Java編譯生成的TestJNI.h加入到C/C++的工程中,否則上面代碼的include "TestJNI.h"無法引用到
2、編譯上面的testdll.cpp文件,可以生成testdll.dll文件,但是這過程通常會出現:在TestJNI.h中找不到jni.h
解決方法:把Javajdk中的下面這些.h文件複製一份到Microsoft Visual Studio的安裝目錄下的\VC\include文件夾下,例如筆者的是D:\Program Files\Microsoft Visual Studio 10.0\VC\include
- \jdk\include\jni.h
- \jdk\include\win32\jawt_md.h
- \jdk\include\win32\jni_md.h
三、結合起來
1、生成的DLL文件的名稱要跟Java代碼中需要調用的testdll一樣,如果不同可以修改文件名,這裏是testdll.dll
2、然後把該dll文件拷貝到TestJNI.class文件的目錄下。
3、使用java testdll 運行它,就可以觀察到結果
說明:如果運行java testdll 出現錯誤:“ 找不到或無法加載主類”,可能是環境變量的classpath鍵值的分號;掉了,一般掉了這個就會出現這種問題
四、最後對TestJNI.h的內容理解補充
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- /* Header for class TestJNI*/
- #ifndef _Included_TestJNI
- #define _Included_TestJNI
- #ifdef __cplusplus
- extern "C" {
- #endif
- /*
- * Class: TestJNI
- * Method: get
- * Signature: ()I
- */
- JNIEXPORT jint JNICALL Java_TestJNI_get (JNIEnv *, jclass);
- /*
- * Class: TestJNI
- * Method: set
- * Signature: (I)V
- */
- JNIEXPORT void JNICALL Java_TestJNI_set (JNIEnv *, jclass, jint);
- #ifdef __cplusplus
- }
- #endif
- #endif
JNIEXPORT jint JNICALL Java_TestJNI_get (JNIEnv *, jclass); 和
JNIEXPORT void JNICALL Java_TestJNI_set (JNIEnv *, jclass, jint);
這裏JNIEXPORT和JNICALL都是JNI的關鍵字,表示此函數是要被JNI調用的。而jint是以JNI爲中介使JAVA的int類型與本地的int溝通的一種類型,我們可以視而不見,就當做int使用。函數的名稱是JAVA_再加上java程序的package路徑再加函數名組成的。參數中,我們也只需要關心在JAVA程序中存在的參數,至於JNIEnv*和jclass我們一般沒有必要去碰它。