最近研究Android,,涉及到JNI調(diào)用。雖然我對Java語言有所了解,,但是還沒有接觸過JNI,。今天在Unix Center的Ubuntu上面用JNI實現(xiàn)了“經(jīng)典”的“Hello world”程序,。通過這個簡單的程序把JNI的一些小知識進行一下總結(jié)。
什么是JNIJNI是Java native interface的簡寫,,可以譯作Java原生接口,。Java可以通過JNI調(diào)用C/C++的庫,這對于那些對性能要求比較高的Java程序無疑是一個福音,。 使用JNI也是有代價,。大家都知道JAVA程序是運行在JVM之上的,可以做到平臺無關(guān),。但是如果Java程序通過JNI調(diào)用了原生的代碼(比如c/c++等),,則Java程序就喪失了平臺無關(guān)性。最起碼需要重新編譯原生代碼部分,。所以應(yīng)用JNI需要好好權(quán)衡,,不到萬不得已,請不要選擇JNI,,可以選擇替代方案,,比如TCP/IP進行進程間通訊等等。這也是為什么谷歌的Android平臺的底層雖然用JNI實現(xiàn),,但是他不建議開發(fā)人員用JNI來開發(fā)Android上面的應(yīng)用的原因,。將會喪失Android上面的應(yīng)用程序平臺無關(guān)性。 JNI的簡單示例下面我就用JNI實現(xiàn)一個經(jīng)典的“Hello World”程序,。該程序在Java中通過JNI調(diào)用c函數(shù)實現(xiàn)“Hello World”的輸出,。創(chuàng)建該程序分為以下步驟: 1、創(chuàng)建一個Java程序(HelloWorld.java)定義原生的c/c++函數(shù),。 2,、用javac編譯HelloWorld.java生成HelloWorld.class。 3,、用javah帶-jni參數(shù)編譯HelloWorld.class生成HelloWorld.h文件,,該文件中定義了c的函數(shù)原型。在實現(xiàn)c函數(shù)的時候需要,。 4,、創(chuàng)建HelloWorld.c,實現(xiàn)HelloWorld.h定義的函數(shù),。 5,、編譯HelloWorld.c生成libHelloWorld.so。 6,、在java虛擬機運行java程序HelloWorld,。 下面我們就一步一步來實現(xiàn)這個程序。 創(chuàng)建HelloWorld.javaclass HelloWorld { private native void print(); public static void main(String[] args) { new HelloWorld().print(); } static { System.loadLibrary("HelloWorld"); } } 注意print方法的聲明,,關(guān)鍵字native表明該方法是一個原生代碼實現(xiàn)的,。另外注意static代碼段的System.loadLibrary調(diào)用,,這段代碼表示在程序加載的時候,自動加載libHelloWorld.so庫,。 編譯HelloWorld.java在命令行中運行如下命令: javac HelloWorld.java 在當(dāng)前文件夾編譯生成HelloWorld.class,。 生成HelloWorld.h在命令行中運行如下命令: javah -jni HelloWorld 在當(dāng)前文件夾中會生成HelloWorld.h。打開HelloWorld.h將會發(fā)現(xiàn)如下代碼: /* 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: print * Signature: ()V */ JNIEXPORT void JNICALL Java_HelloWorld_print (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif 該文件中包含了一個函數(shù)Java_HelloWorld_print的聲明,。這里面包含兩個參數(shù),,非常重要,后面講實現(xiàn)的時候會講到,。 實現(xiàn)HelloWorld.c創(chuàng)建HelloWorld.c文件輸入如下的代碼: #include <jni.h> #include <stdio.h> #include "HelloWorld.h" JNIEXPORT void JNICALL Java_HelloWorld_print(JNIEnv *env, jobject obj) { printf("Hello World!\n"); } 注意必須要包含jni.h頭文件,,該文件中定義了JNI用到的各種類型,宏定義等,。 另外需要注意Java_HelloWorld_print的兩個參數(shù),,本例比較簡單,不需要用到這兩個參數(shù),。但是這兩個參數(shù)在JNI中非常重要,。 env代表java虛擬機環(huán)境,,Java傳過來的參數(shù)和c有很大的不同,,需要調(diào)用JVM提供的接口來轉(zhuǎn)換成C類型的,就是通過調(diào)用env方法來完成轉(zhuǎn)換的,。 obj代表調(diào)用的對象,,相當(dāng)于c++的this。當(dāng)c函數(shù)需要改變調(diào)用對象成員變量時,,可以通過操作這個對象來完成,。 編譯生成libHelloWorld.so在Linux下執(zhí)行如下命令來完成編譯工作: cc -I/usr/lib/jvm/java-6-sun/include/linux/ -I/usr/lib/jvm/java-6-sun/include/ -fPIC -shared -o libHelloWorld.so HelloWorld.c 在當(dāng)前目錄生成libHelloWorld.so。注意一定需要包含Java的include目錄(請根據(jù)自己系統(tǒng)環(huán)境設(shè)定),,因為Helloworld.c中包含了jni.h,。 另外一個值得注意的是在HelloWorld.java中我們LoadLibrary方法加載的是“HelloWorld”,可我們生成的Library卻是libHelloWorld,。這是Linux的鏈接規(guī)定的,,一個庫的必須要是:lib+庫名+.so。鏈接的時候只需要提供庫名就可以了,。 運行Java程序HelloWorld 大功告成最后一步,,驗證前面的成果的時刻到了: java HelloWorld 如果你這步發(fā)生問題,如果這步你收到j(luò)ava.lang.UnsatisfiedLinkError異常,,可以通過如下方式指明共享庫的路徑: java -Djava.library.path='.' HelloWorld 當(dāng)然還有其他的方式可以指明路徑請參考《在Linux平臺下使用JNI》,。 我們可以看到久違的“Hello world!”輸出了。 總結(jié)本文只是一個JNI簡單調(diào)用,,當(dāng)然JNI還有很多東西需要學(xué)習(xí),。如有錯誤之處請不吝指教,。 參考文章: The Java Native Interface Programmer’s Guide and Specification |
|