1.關於native關鍵字
想必讀者已經瞭解過native關鍵字了。這裏筆者就大致囊括一下,被native關鍵字修飾的方法叫做本地方法,本地方法和其它方法不一樣,本地方法意味着和平臺有關,因此使用了native的程序可移植性都不太高。另外native方法在JVM中運行時數據區也和其它方法不一樣,它有專門的本地方法棧。native方法主要用於加載文件和動態鏈接庫,由於Java語言無法訪問操作系統底層信息(比如:底層硬件設備等),這時候就需要藉助C語言來完成了。被native修飾的方法可以被C語言重寫。
2.使用native關鍵字
2.1 使用步驟
- Java程序中聲明native修飾的方法,類似於abstract修飾的方法,只有方法簽名,沒有方法實現。編譯該java文件,會產生一個.class文件。
- 使用javah編譯上一步產生的class文件,會產生一個.h文件。
- 寫一個.cpp文件實現上一步中.h文件中的方法。
- 將上一步的.cpp文件編譯成動態鏈接庫文件.dll。
- 最後就可以使用System或是Runtime中的loadLibrary()方法加載上一步的產生的動態連接庫文件了。
2.2 案例
爲了更好理解,該案例的所有都在文件在 D:\JNI\ 目錄下。
2.2.1 編寫.java文件
public class HelloWorld{
public native void h();//該方法和abstract修飾的方法一樣,只有簽名。
static{
System.loadLibrary("hello");//不寫文件的後綴,程序會自動加上.dll的。
}
public static void main(String[] args){
new HelloWorld().h();//調用
}
}
2.2.2 編譯.java文件
在CMD中編譯該程序 javac HelloWorld.java ,就會產生一個HelloWorld.class文件。
2.2.3 獲得.h文件
將第二步中產生的字節碼文件,通過 javah -jni HelloWorld 就會產生一個HelloWorld.h文件。
我們用記事本打開HelloWorld.h文件
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */
#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloWorld
* Method: h
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloWorld_h
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
可以看出,在HelloWorld.java文件中的h()方法已經變成了 JNIEXPORT void JNICALL Java_HelloWorld_h (JNIEnv *, jobject); ,方法名是原來的 包名_類名_方法名 。在該文件中還引用了 jni.h 文件。
2.2.4 編寫hello.cpp文件
編寫hello.cpp文件的方式有許多,可以利用Visual Studio軟件,因爲最後需要生成dll文件,因此在下載Visual Studio之前應該查一查版本是否能夠生成自己電腦需要的dll版本(32位dll或64爲dll)。這裏下載的是vs2013,該版本既可以生成32的dll,由可以生成64位的dll。關於vs2013生成dll可以參考Visual Studio 2013生成64位dll
因爲我們在第一步中調用的文件名稱爲hello,所以這裏的.cpp文件必須爲 hello.cpp 文件。這裏筆者的文件如下:
// hello.cpp : 定義 DLL 應用程序的導出函數。
//
#include "stdafx.h"
#include "HelloWorld.h"
JNIEXPORT void JNICALL Java_HelloWorld_h(JNIEnv *, jobject) {
printf("Hello! ");//打印信息
}
可以看出引入了 HelloWorld.h 文件,所以hello.cpp 文件應該和 HelloWorld.h文件在同一個目錄下面。如果讀者現在編譯hello.cpp文件會報錯 “jni.h”: No such file or directory 。在 HelloWorld.h 文件中我們引入了 jni.h文件,所以也應該把 jni.h 文件放到同一級目錄下面,關於這個文件和相關的文件讀者可以到JDK的安裝目錄下面的include下面查找,更多信息可以查看JDk、JRE、JVM的關係。還應該把 HelloWorld.h 文件中的 #include <jni.h> 改爲 #include “jni.h” 。最後生成 hello.dll 文件就可以了
2.2.5 部署hello.dll文件
我們使用了 System.loadLibary(“hello”); 加載動態鏈接庫,這個加載路徑是按照java.libary.path進行查詢的,讀者可以根據System.getProperty(“java.libary.path”)驗證,該路徑就是環境變量中的path路徑。網上有好多說直接把hello.dll仍在 C:\Windows\System32 路徑下。不過筆者建議,先應該查看自己環境變量path的值,那麼把hello.dll放到path中配置的第一個路徑下。
2.2.6 運行HelloWorld.class文件
我們回到 D:/JNI 路徑下,使用 java HelloWorld 就成功調用動態連接庫了。
筆者的控制檯上成功打印了hello!。筆者對這裏加載的理解,就是利用反射機制,在運行的時候找到hello.dll文件並且解析,根據動態鏈接庫中的文件名稱創建出對象和方法,然後我們就可以利用對象調用方法了。上面的HelloWorld.java文件,創建動態鏈接庫和調用方法都在同一個類中,這樣的話一個只需要使用這個類的對象調用方法就可以通過編譯和運行了。如果我們引入的是被人的.cpp文件,那麼根據.cpp文件中的方法名,在需要的地方做適當調整就可以調用了。