Linux下JNI的使用


 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

     

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM