1. 程式人生 > >JAVA JNI開發應用例項

JAVA JNI開發應用例項

一、基礎理論

百科介紹:JNI是Java Native Interface的縮寫,它提供了若干的API實現了Java和其他語言的通訊(主要是C&C++)。從Java1.1開始,JNI標準成為java平臺的一部分,它允許Java程式碼和其他語言寫的程式碼進行互動。JNI一開始是為了本地已編譯語言,尤其是C和C++而設計的,但是它並不妨礙你使用其他程式語言,只要呼叫約定受支援就可以了。使用java與本地已編譯的程式碼互動,通常會喪失平臺可移植性。但是,有些情況下這樣做是可以接受的,甚至是必須的。例如,使用一些舊的庫,與硬體、作業系統進行互動,或者為了提高程式的效能。JNI標準至少要保證原生代碼能工作在任何Java 虛擬機器環境。

JNI的應用限制

一旦使用JNI,JAVA程式就喪失了JAVA平臺的兩個優點:
1、程式不再跨平臺。要想跨平臺,必須在不同的系統環境下重新編譯本地語言部分。
2、程式不再是絕對安全的,原生代碼的不當使用可能導致整個程式崩潰。一個通用規則是,你應該讓本地方法集中在少數幾個類當中。這樣就降低了JAVA和C之間的耦合性。

應用場合

當你開始著手準備一個使用JNI的專案時,請確認是否還有替代方案。應用程式使用JNI會帶來一些副作用。下面給出幾個方案,可以避免使用JNI的時候,達到與原生代碼進行互動的效果: 1、JAVA程式和本地程式使用TCP/IP或者IPC進行互動。 2、當用JAVA程式連線本地資料庫時,使用
JDBC
提供的API。 3、JAVA程式可以使用分散式物件技術,如JAVA IDL API。 這些方案的共同點是,JAVA和C處於不同的執行緒,或者不同的機器上。這樣,當本地程式崩潰時,不會影響到JAVA程式。 下面這些場合中,同一程序內JNI的使用無法避免: 1、程式當中用到了JAVA API不提供的特殊系統環境才會有的特徵。而跨程序操作又不現實。 2、你可能想訪問一些己有的本地庫,但又不想付出跨程序呼叫時的代價,如效率,記憶體,資料傳遞方面。 3、JAVA程式當中的一部分程式碼對效率要求非常高,如演算法計算,圖形渲染等。 總之,只有當你必須在同一程序中呼叫原生代碼時,再使用JNI。

實現步驟

·編寫帶有native宣告的方法的java類 ·使用javac命令編譯所編寫的java類,然後使用javah + java類名生成副檔名為h的標頭檔案 ·使用C/C++實現本地方法 ·將C/C++編寫的檔案生成動態連線庫 1) 編寫java程式:這裡以HelloWorld為例。 程式碼1:
1 2 3 4 5 6 7 8 9 public class HelloWorld { public native void displayHelloWorld();//所有native關鍵詞修飾的都是對本地的宣告 static { System.loadLibrary("hello");//載入本地庫 } public static void main(String[] args) { new HelloWorld().displayHelloWorld(); } }
宣告native方法:如果你想將一個方法做為一個本地方法的話,那麼你就必須宣告該方法為native的,並且不能實現。其中方法的引數和返回值在後面講述。 Load動態庫:System.loadLibrary("hello");載入動態庫(我們可以這樣理解:我們的方法 displayHelloWorld()沒有實現,但是我們在下面就直接使用了,所以必須在使用之前對它進行初始化)這裡一般是以static塊進行載入的。同時需要注意的是System.loadLibrary();的引數“hello”是動態庫的名字。 2) 編譯 沒有什麼好說的了javac HelloWorld.java 3) 生成副檔名為h的標頭檔案javah HelloWorld jni HelloWorld 標頭檔案的內容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 /*DO NOT EDI TTHIS FILE - it is mach inegenerated*/ #include<jni.h> /*Header for class HelloWorld*/ #ifndef_Included_HelloWorld #define_Included_HelloWorld #ifdef__cplusplus extern"C"{ #endif /* *Class:HelloWorld *Method:displayHelloWorld *Signature:()V */ JNIEXPORTvoidJNICALL Java_HelloWorld_displayHelloWorld(JNIEnv*,jobject); #ifdef__cplusplus } #endif #endif
JNIJNI (這裡我們可以這樣理解:這個h檔案相當於我們在java裡面的介面,這裡聲明瞭一個Java_HelloWorld_displayHelloWorld (JNIEnv *, jobject);方法,然後在我們的本地方法裡面實現這個方法,也就是說我們在編寫C/C++程式的時候所使用的方法名必須和這裡的一致)。 4) 編寫本地方法實現和由javah命令生成的標頭檔案裡面宣告的方法名相同的方法。 程式碼2:
1 2 3 4 5 6 7 8 9 10 11 #include"jni.h" #include"HelloWorld.h" //#includeotherheaders JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld(JNIEnv*env,jobject obj) { printf("Helloworld!\n"); return; }
JNIJNI 注意程式碼2中的第1行,需要將jni.h(該檔案可以在%JAVA_HOME%/include資料夾下面找到)檔案引入,因為在程式中的JNIEnv、 jobject等型別都是在該標頭檔案中定義的;另外在第2行需要將HelloWorld.h標頭檔案引入(我是這麼理解的:相當於我們在編寫java程式的時候,實現一個介面的話需要宣告才可以,這裡就是將HelloWorld.h標頭檔案裡面宣告的方法加以實現。當然不一定是這樣)。然後儲存為 HelloWorldImpl.c就ok了。 5) 生成動態庫 這裡以在Windows中為例,需要生成dll檔案。在儲存HelloWorldImpl.c資料夾下面,使用VC編譯器cl成。 cl -I%java_home%\include -I%java_home%\include\win32 -LD HelloWorldImp.c -Fehello.dll 注意:生成的dll檔名在選項-Fe後面配置,這裡是hello,因為在HelloWorld.java檔案中我們loadLibary的時候使用的名字是hello。當然這裡修改之後那裡也需要修改。另外需要將-I%java_home%\include -I%java_home%\include\win32引數加上,因為在第四步裡面編寫本地方法的時候引入了jni.h檔案。 如果配置了MinGW,也可以這樣來編譯:gcc -Wall -D_JNI_IMPLEMENTATION_ -Wl,--kill-at -Id:/java/include –Id:/java/include/win32 -shared -o (輸出的dll檔名,如sum.dll) (輸入的c/c++原始檔,如abc.c)。 6) 執行程式 javaHelloWorld就ok. 如果用eclipse,需將dll或so檔案放在專案下,而不是src及其子目錄下。 如果用命令列編譯,把dll檔案放在該包的同目錄下。[1]

二、應用實踐

三、參考資料