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進行進程間通訊等等。這也是為什么谷歌的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 __cplusplus extern "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
簡單例子,照着以下參考文檔即可實現。
參考文檔:
http://my.unix-center.net/~Simon_fu/?p=359
http://www.ibm.com/developerworks/cn/java/l-linux-jni/
JNIEnv功能參考:
http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/functions.html