閱讀564 返回首頁    go 阿裏雲 go 技術社區[雲棲]


如何在Android下使用JNI

1.引言

我們知道,Android係統的底層庫由c/c++編寫,上層Android應用程序通過Java虛擬機調用底層接口,銜接底層c/c++庫與Java應用程序間的接口正是JNIJavaNative Interface)。本文描述了如何在ubuntu下配置AndroidJNI的開發環境,以及如何編寫一個簡單的c函數庫和JNI接口,並通過編寫Java程序調用這些接口,最終運行在模擬器上的過程。


2.環境配置


2.1.安裝jdk1.6

1)從jdk官方網站https://www.oracle.com/technetwork/java/javase/downloads/jdk-6u29-download-513648.html下載jdk-6u29-linux-i586.bin文件。

2)執行jdk安裝文件

[html] view plaincopy
  1. $chmod a+x jdk-6u29-linux-i586.bin  
  2. $jdk-6u29-linux-i586.bin  

(3)配置jdk環境變量

[html] view plaincopy
  1. $sudo vim /etc/profile  
  2. #JAVAEVIRENMENT  
  3. exportJAVA_HOME=/usr/lib/java/jdk1.6.0_29  
  4. exportJRE_HOME=$JAVA_HOME/jre  
  5. exportCLASSPATH=$JAVA_HOME/lib:$JRE_HOME/lib:$CLASSPATH  
  6. exportPATH=$JAVA_HOME/bin:$JRE_HOME/bin:$PATH  

保存後退出編輯,並重啟係統。

4)驗證安裝

[html] view plaincopy
  1. $java -version  
  2. javaversion "1.6.0_29"  
  3. Java(TM)SE Runtime Environment (build 1.6.0_29-b11)  
  4. JavaHotSpot(TM) Server VM (build 20.4-b02, mixed mode)  
  5. $javah  
  6. 用法:javah[選項]<>  
  7. 其中[選項]包括:  
  8. -help輸出此幫助消息並退出  
  9. -classpath<路徑>用於裝入類的路徑  
  10. -bootclasspath<路徑>用於裝入引導類的路徑  
  11. -d<目錄>輸出目錄  
  12. -o<文件>輸出文件(隻能使用-d或-o中的一個)  
  13. -jni生成JNI樣式的頭文件(默認)  
  14. -version輸出版本信息  
  15. -verbose啟用詳細輸出  
  16. -force始終寫入輸出文件  
  17. 使用全限定名稱指定<>(例  
  18. 如,java.lang.Object)。  

2.2.安裝android應用程序開發環境

ubuntu下安裝android應用程序開發環境與windows類似,依次安裝好以下軟件即可:

1Eclipse

2ADT

3AndroidSDK

windows下安裝唯一不同的一點是,下載這些軟件的時候要下載Linux版本的安裝包。

安裝好以上android應用程序的開發環境後,還可以選擇是否需要配置emulatoradb工具的環境變量,以方便在進行JNI開發的時候使用。配置步驟如下:

emulator所在目錄android-sdk-linux/tools以及adb所在目錄android-sdk-linux/platform-tools添加到環境變量中,android-sdk-linuxandroidsdk安裝包android-sdk_rxx-linux的解壓目錄。

[plain] view plaincopy
  1. $sudo vim /etc/profile  
  2. exportPATH=~/software/android/android-sdk-linux/tools:$PATH  
  3.  exportPATH=~/software/android/android-sdk-linux/platform-tools:$PATH  

編輯完畢後退出,並重啟生效。


2.3.安裝NDK

NDK是由android提供的編譯android本地代碼的一個工具。

1)從androidndk官網https://developer.android.com/sdk/ndk/index.html下載ndk,目前最新版本為android-ndk-r6b-linux-x86.tar.bz2.

2)解壓ndk到工作目錄:

[plain] view plaincopy
  1. $tar -xvf android-ndk-r6b-linux-x86.tar.bz2  
  2. $sudo mv android-ndk-r6b /usr/local/ndk  

3)設置ndk環境變量

[plain] view plaincopy
  1. $sudo vim /etc/profile  
  2. exportPATH=/usr/local/ndk:$PATH  

編輯完畢後保存退出,並重啟生效

4)驗證安裝

[plain] view plaincopy
  1. $ cd/usr/local/ndk/samples/hello-jni/  
  2. $ ndk-build  
  3. Gdbserver : [arm-linux-androideabi-4.4.3] libs/armeabi/gdbserver  
  4. Gdbsetup : libs/armeabi/gdb.setup  
  5. Install : libhello-jni.so => libs/armeabi/libhello-jni.so  

3.JNI實現

我們需要定義一個符合JNI接口規範的c/c++接口,這個接口不用太複雜,例如輸出一個字符串。接下來,則需要把c/c++接口的代碼文件編譯成共享庫(動態庫).so文件,並放到模擬器的相關目錄下。最後,啟動Java應用程序,就可以看到最終效果了。

3.1.編寫Java應用程序代碼

1)啟動Eclipse,新建android工程

ProjectJNITest

Packageorg.tonny.jni

ActivityJNITest

2)編輯資源文件


編輯res/values/strings.xml文件如下:

[html] view plaincopy
  1. <?xmlversionxmlversion="1.0"encoding="utf-8"?>  
  2. <resources>  
  3. <stringnamestringname="hello">HelloWorld, JNITestActivity!</string>  
  4. <stringnamestringname="app_name">JNITest</string>  
  5. <stringnamestringname="btn_show">Show</string>  
  6. </resources>  

編輯res/layout/main.xml文件如下:


[html] view plaincopy
  1. <?xmlversionxmlversion="1.0"encoding="utf-8"?>  
  2. <LinearLayoutxmlns:androidLinearLayoutxmlns:android="https://schemas.android.com/apk/res/android"  
  3. android:layout_width="fill_parent"  
  4. android:layout_height="fill_parent"  
  5. android:orientation="vertical">  
  6. <TextView  
  7. android:layout_width="fill_parent"  
  8. android:layout_height="wrap_content"  
  9. android:text="@string/hello"/>  
  10. <EditText  
  11. android:id="@+id/ed_name"  
  12. android:layout_width="match_parent"  
  13. android:layout_height="wrap_content"  
  14. android:layout_gravity="center_horizontal"  
  15. android:layout_marginLeft="5dp"  
  16. android:layout_marginRight="5dp"/>  
  17. <Button  
  18. android:id="@+id/btn_show"  
  19. android:layout_width="109dp"  
  20. android:layout_height="wrap_content"  
  21. android:layout_gravity="center_horizontal"  
  22. android:text="@string/btn_show"/>  
  23. </LinearLayout>  

我們在主界麵上添加了一個EditText控件和一個Button控件。


3)編輯JNITest.java文件

[java] view plaincopy
  1. packageorg.tonny.jni;  
  2.   
  3. importandroid.app.Activity;  
  4. importandroid.os.Bundle;  
  5. importandroid.view.View;  
  6. importandroid.widget.EditText;  
  7. importandroid.widget.Button;  
  8.   
  9.   
  10. publicclassJNITestextendsActivity {  
  11. static{  
  12. System.loadLibrary("JNITest");  
  13. }  
  14. privatenativeString GetReply();  
  15. privateEditTextedtName;  
  16. privateButtonbtnShow;  
  17. Stringreply;  
  18. /**Called when the activity is first created. */  
  19. @Override  
  20. publicvoidonCreate(Bundle savedInstanceState) {  
  21. super.onCreate(savedInstanceState);  
  22. setContentView(R.layout.main);  
  23. reply= GetReply();  
  24. edtName= (EditText)this.findViewById(R.id.ed_name);  
  25. btnShow= (Button)this.findViewById(R.id.btn_show);  
  26. btnShow.setOnClickListener(newButton.OnClickListener() {  
  27. publicvoidonClick(View arg0) {  
  28. edtName.setText(reply);  
  29. }  
  30. });  
  31. }  
  32. }  


我們看這一段代碼:

[java] view plaincopy
  1. static{  
  2. System.loadLibrary("JNITest");  
  3. }  

static表示在係統第一次加載類的時候,先執行這一段代碼,在這裏表示加載動態庫libJNITest.so文件。


再看這一段:

[java] view plaincopy
  1. privatenativeString GetReply();  

native表示這個方法由本地代碼定義,需要通過jni接口調用本地c/c++代碼。

[java] view plaincopy
  1. publicvoidonClick(View arg0) {  
  2. edtName.setText(reply);  
  3. }  

這段代碼表示點擊按鈕後,把native方法的返回的字符串顯示到EditText控件。


4)編譯工程,生成.class文件。


3.2.用javah工具生成符合JNI規範的c語言頭文件


在終端中,進入android工程所在的bin目錄

[plain] view plaincopy
  1. $cd ~/project/Android/JNITest/bin  

我們用ls命令查看,可以看到bin目錄下有個classes目錄,其目錄結構為classes/org/tonny/jni,即classes的子目錄結構是android工程的包名org.tonny.jni。請注意,下麵我們準備執行javah命令的時候,必須進入到org/tonny/jni的上級目錄,即classes目錄,否則javah會提示找不到相關的java類。

下麵繼續:

[plain] view plaincopy
  1. $cd classes  
  2. $javah org.tonny.jni.JNITest  
  3. $ls  
  4. org org_tonny_jni_JNITest.h  

執行javahorg.tonny.jni.JNITest命令,在classes目錄下會生成org_tonny_jni_JNITest.h頭文件。如果不進入到classes目錄下的話,也可以這樣:

[plain] view plaincopy
  1. $javah -classpath ~/project/Android/JNITest/bin/classesorg.tonny.jni.JNITest  

-classpath 參數表示裝載類的目錄。


3.3.編寫c/c++代碼

生成org_tonny_jni_JNITest.h頭文件後,我們就可以編寫相應的函數代碼了。下麵在android工程目錄下新建jni目錄,即~/project/Android/JNITest/jni,把org_tonny_jni_JNITest.h頭文件拷貝到jni目錄下,並在jni目錄下新建org_tonny_jni_JNITest.c文件,編輯代碼如下:

  1. #include<jni.h>  
  2. #include<string.h>  
  3. #include"org_tonny_jni_JNITest.h"  
  4.   
  5.   
  6. JNIEXPORTjstring JNICALLJava_org_tonny_jni_JNITest_GetReply  
  7. (JNIEnv *env, jobject obj){  
  8. return(*env)->NewStringUTF(env,(char*)"Hello,JNITest");  
  9. }  

我們可以看到,該函數的實現相當簡單,返回一個字符串為:"Hello,JNITest"

3.4.編寫Android.mk文件

~/project/Android/JNITest/jni目錄下新建Android.mk文件,android可以根據這個文件的編譯參數編譯模塊。編輯Android.mk文件如下:

[plain] view plaincopy
  1. LOCAL_PATH:= $(call my-dir)  
  2. include$(CLEAR_VARS)  
  3. LOCAL_MODULE := libJNITest  
  4. LOCAL_SRC_FILES:= org_tonny_jni_JNITest.c  
  5. include$(BUILD_SHARED_LIBRARY)  

LOCAL_MODULE表示編譯的動態庫名稱

LOCAL_SRC_FILES 表示源代碼文件

3.5.用ndk工具編譯並生成.so文件

進入到JNITest的工程目錄,執行ndk-build命令即可生成libJNITest.so文件。

[plain] view plaincopy
  1. $cd ~/project/Android/JNITest/  
  2. $ndk-build  
  3. Invalidattribute name:  
  4. package  
  5. Install : libJNITest.so => libs/armeabi/libJNITest.so  

可以看到,在工程目錄的libs/armeabi目錄下生成了libJNITest.so文件。

3.6.在模擬器上運行

1)首先,我們把android模擬器啟動起來。進入到emulator所在目錄,執行emulator命令:

[plain] view plaincopy
  1. $cd ~/software/android/android-sdk-linux/tools  
  2. $./emulator @AVD-2.3.3-V10 -partition-size 512  

AVD-2.3.3-V10表示你的模擬器名稱,與在Eclipse->AVDManager下的AVDName對應,-partition-size表示模擬器的存儲設備容量。


2)接下來,我們需要把libJNITest.so文件拷貝到模擬器的/system/lib目錄下,執行以下命令:

[plain] view plaincopy
  1. $cd ~/project/Android/JNITest/libs/armeabi/  
  2. $adb remount  
  3. $adb push libJNITest.so /system/lib  
  4. 80 KB/s (10084 bytes in 0.121s)  

當在終端上看到有80 KB/s (10084 bytes in 0.121s)傳輸速度等信息的時候,說明拷貝成功。


3)在終端上執行JNITest程序,這個我們可以在Eclipse下,右鍵點擊JNITest工程,RunAs->Android Application,即可在模擬器上啟動程序,執行效果如下:



在模擬器上點擊【Show】按鈕,即可看到Hello,JNITest,而這個字符串正是我們在org_tonny_jni_JNITest.c代碼文件中所定義的。

OK,現在大功告成!


4.參考文章

AndroidJNI開發入門之一

AndroidJni代碼示例講解

最後更新:2017-04-03 12:55:58

  上一篇:go Android 帶清除功能的輸入框控件ClearEditText,仿IOS的輸入框
  下一篇:go 如何在android的jni線程中實現回調