Linux下 JNI的使用
學習Android其中涉及對JNI的使用,對於這種跨語言的調用真沒有見過,
Java也都是最近才學的更別說對JNI的瞭解了,
JNI的使用對於Android來說又是十分的重要和關鍵。那麼到底Java到底是如何調用C/C++的,
通過網路達人的總結中學習,自己也順便總結一下這個學習的過程。
什麼是JNI
JNI是Java native interface的簡寫,可以譯作Java原生介面。
Java可以通過JNI調用C/C++的庫,這對於那些對效能要求比較高的Java程式無疑是一個福音。
JNI是Java與C/C++互動的介面。
使用JNI也是有代價。大家都知道JAVA程式是運行在JVM之上的,可以做到平台無關。
但是如果Java程式通過JNI調用了原生的代碼(比如c/c++等),則Java程式就喪失了平台無關性。
最起碼需要重新編譯原生代碼部分。所以應用JNI需要好好權衡,不到萬不得已,請不要選擇JNI,
可以選擇替代方案,比如TCP/IP進行進程間通訊等等。這也是為什麼Google的Android平台的底層雖然用JNI實現,
但是他不建議開發人員用JNI來開發Android上面的應用的原因。將會喪失Android上面的應用程式平台無關性。
代碼執行個體
下面以HelloWorld的實現學習Linux下 JNI的使用。
第一步:
建立一個 TestJni.java檔案
import java.util.*;public class TestJni{ //聲明原生函數:參數為String類型 public native void print(String content); //載入本地庫代碼 static { System.loadLibrary("TestJni"); }}
編譯 TestJni.java檔案:javac TestJni.java
在當前檔案夾下產生TestJni.class檔案
注意print方法的聲明,關鍵字native表明該方法是一個原生代碼實現的。
另外注意static程式碼片段的System.loadLibrary調用,這段代碼錶示在程式載入的時候,自動載入libTestJni.so庫。
第二步:
產生 TestJni.h檔案
執行命令:javah -jni TestJni
產生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 __cplusplusextern "C" {#endif/* * Class: TestJni * Method: print * Signature: (Ljava/lang/String;)V */JNIEXPORT void JNICALL Java_TestJni_print (JNIEnv *, jobject, jstring);#ifdef __cplusplus}#endif#endif
該檔案中包含了一個函數Java_TestJni_print的聲明。這裡面自動包含兩個參數,非常重要。JNIEnv *和 jobject
第三步:
建立TestJni.c檔案
#include <jni.h>#include <stdio.h>#include <TestJni.h>JNIEXPORT void JNICALL Java_TestJni_print(JNIEnv *env,jobject obj, jstring content){ // 從 instring 字串取得指向字串 UTF 編碼的指標 //注意C語言必須(*env)-> C++ env-> const jbyte *str = (const jbyte *)(*env)->GetStringUTFChars(env,content, JNI_FALSE); printf("Hello---->%s\n",str); // 通知虛擬機器本地代碼不再需要通過 str 訪問 Java 字串。 (*env)->ReleaseStringUTFChars(env, content, (const char *)str ); return;}
//這裡看到 JNIEnv作用了,使得我們可以使用Java的方法
//jobject 指向在此 Java 代碼中執行個體化的 Java 對象 LocalFunction 的一個控制代碼,相當於 this 指標
參數類型 jstring 對應java中的String,這裡是有所不同的。每一個Java裡的類型這裡有對應的與之匹配。
命令列輸入:
cc -I/usr/lib/jvm/java-6-sun/include/linux/
-I/usr/lib/jvm/java-6-sun/include/
-I/home/xmp/AndroidProject/apk/JNI
-fPIC -shared -o libTestJni.so TestJni.c
產生:libTestJni.so庫檔案
在目前的目錄產生libTestJni.so。注意一定需要包含Java的include目錄(請根據自己系統內容設定),
因為libTestJni.c中包含了jni.h。另外一個值得注意的是在libTestJni.java中我們LoadLibrary方法載入的是“TestJni”,
可我們產生的Library卻是libTestJni。這是Linux的連結規定的,
一個庫的必須要是:lib+庫名+.so。連結的時候只需要提供庫名就可以了
-I/home/xmp/AndroidProject/apk/JNI 是我自己的練習目錄也必須包含,否則.c檔案中會找不到TestJni.h標頭檔。
現在 liblibTestJni.so就是一個可以使用的庫了,其功能就是有一個print函數 與剛才所建立的TestJni.java檔案對應,
TestJni.java類中載入庫liblibTestJni.so,聲明了其函數print。所以現在TestJni.java中具備使用print函數的功能;
所以現在我們就可以通過使用TestJni.java來使用調用C庫libTestJni.so中的函數
當然現在任何java類都可已載入liblibTestJni.so庫來使用其中的功能。
第四步:
建立HelloWord.java函數
import java.util.*;public class HelloWorld{ public static void main(String argv[]) { new HelloWorld(); } public HelloWorld() { new TestJni().print("Hello,World !"); //調用TestJni的原生函數print }}
輸入命令編譯: javac HelloWorld.java
產生HelloWorld.class
第五步:
運行HelloWorld程式
命令列輸入:java HelloWorld
輸出結果:Hello---->Hello,World !
驗證OK!
如果你這步發生問題,如果這步你收到java.lang.UnsatisfiedLinkError異常,可以通過如下方式指明共用庫的路徑:
java -Djava.library.path='.' HelloWorld
或者輸入命令:
export LD_LIBRARY_PATH=“HelloWorld路徑”:$LD_LIBRARY_PATH 設定環境變數
然後再 java HelloWorld 一樣OK
簡單例子,照著以下參考文檔即可實現。