這是一個經典的helloworld程序,此程序本身並沒有什麼功能,但是其意義在於拋磚引玉。瞭解了JNI的機理,對於那些習慣於C程序的人很重要,在處理底層的讀寫等操作就可以在UI層用java輕而易舉地實現,就
如同C程序中調用一個系統調用那麼簡單,下面分步敘述。
一、創建最上層的java程序
首先打開eclipse,創建一個java工程,並在工程中添加一個類,類名爲helloworld.java,如圖所示:
打開java源碼helloworld.java,編寫代碼,實現c語言中的printf功能,代碼如下:
Print()函數的聲明中,關鍵字native標明此方法是一個“本地”方法或者“原生”方法,即從c/c++庫中調用的方法。
Main()函數必須爲static,否則運行時會報錯,找不到main,原因不詳。
Static段中的loadLibrary提供了此程序運行時需要加載的c動態鏈接庫***.so,print()的實現就在此庫中。
二、
編譯helloworld.java
編譯helloworld.java的方法有兩種,一種是在shell中利用javac編譯,另一種在eclipse中,直接保存。
對於第一種方法,直接告訴javac編譯器需要編譯的文件即可,命令如下:
Javac helloworld.java
生成的helloworld.class就在$(shell pwd)下。
第二種方法,eclipse會在工程目錄下的bin目錄下生成helloworld.class。
三、
生成JNI頭文件helloworld.h
利用javah命令生成。Javah的命令格式如下:
javah –jni –classpath <classpath> <classfile>
例如我的helloworld.class文件是由eclipse生成的,因此其絕對路徑是:
/root/Project/jni/helloworld/bin/helloworld.class
我當前所在目錄爲/root/Project/jni/helloworld/
上面的命令就可以寫成:javah –jni –classpath bin helloworld
命令執行後再$(shell pwd)下生成一個文件:helloworld.h,其內容如下:
在這個文件中提供了java到c的接口
JNIEXPORT void JNICALL Java_helloworld_print(JNIEnv *, jobject);
另外,jni.h中定義了各種從java到c的類型轉換,宏定義等。
接着我在工程根目錄下創建了一個目錄include,將生成的頭文件mv到include目錄下。
四、
創建helloworld.c並編譯生成動態鏈接庫helloworld.so
首先編寫代碼如下(推薦在vim或者其他可視化編輯軟件中):
由圖中可見,此處就是最上層的helloworld.java代碼中的方法的實現,在此實現中沒有用到系統提供的兩個參數,但是此2個參數非常重要。第一個參數代表此共享庫運行的環境,即java虛擬機,在虛擬機中獎上層的java類型轉換爲C類型。第二個參數類似於C++中的this對象,C代碼要通過此函數來改變對象中某些變量的值。
接下來要做的就是將此C代碼編譯成共享鏈接庫文件,可以直接在shell裏完成,或者爲了便於管理,通過GNU make。
編譯時需要定位jni.h所在路徑,locate,結果如下:
由於我當前使用的ndk是~/Project/android-ndk-r4b,並且我想讓程序運行在宿主機上,於是選擇第4條或者第6條,我選第6條,因此在gcc的flags裏應該添加此路徑。
爲了方便起見,我在helloworld根目錄下創建一個makefile,採用遞歸調用Makefile,深入到各目錄中進行操作,對各子目錄下的Makefile統一管理:
源文件所在目錄的Makefile如下:
使用make命令在工程根目錄下編譯之後,在工程根目錄下的lib目錄中生產了helloworld.so的共享庫文件:
五、
檢驗程序正確性
運行程序有兩個方法:shell中運行和eclipse中運行。
1.
shell中運行
運行jni程序需要告訴java兩個參數,即classpath和共享庫的位置。
classpath可以通過設置環境變量:
export CLASSPATH=$CLASSPATH:/root/Project/workspace/jni/helloworld/bin/
而共享庫的位置在命令行中給出即可:
Java –Djava.library.path=’lib/’ helloworld
運行結果如下:
2.
eclipse中運行
需要爲java虛擬機設置共享庫路徑,打開run configurations,切換到第二個選項卡arguments,在虛擬機參數輸入框中輸入
-Djava.library.path=/root/Project/workspace/jni/helloworld/lib/
如圖所示:
之後運行,運行結果如下:
六、
補充說明
我的工程根目錄:/root/Project/workspace/jni/helloworld/
如果用eclipse編寫代碼並運行的話,需要爲eclipse安裝CDT插件,這樣纔可以支持c/c++。對於這個程序,如果不使用eclipse,可能反而更簡單一些。
另外,環境變量的設置也很重要,要根據自己系統中jdk的路徑設置環境變量,設置方法可以上網搜索。