1. 程式人生 > >如何在Android下使用JNI

如何在Android下使用JNI

1.引言

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

2.環境配置

2.1.安裝jdk1.6

2)執行jdk安裝檔案

$chmod a+x jdk-6u29-linux-i586.bin
$jdk-6u29-linux-i586.bin

(3)配置jdk
環境變數
$sudo vim /etc/profile
#JAVAEVIRENMENT
exportJAVA_HOME=/usr/lib/java/jdk1.6.0_29
exportJRE_HOME=$JAVA_HOME/jre
exportCLASSPATH=$JAVA_HOME/lib:$JRE_HOME/lib:$CLASSPATH
exportPATH=$JAVA_HOME/bin:$JRE_HOME/bin:$PATH

儲存後退出編輯,並重啟系統。 4)驗證安裝
$java -version
javaversion "1.6.0_29"
Java(TM)SE Runtime Environment (build 1.6.0_29-b11)
JavaHotSpot(TM) Server VM (build 20.4-b02, mixed mode)
$javah
用法:javah[選項]<類>
其中[選項]包括:
-help輸出此幫助訊息並退出
-classpath<路徑>用於裝入類的路徑
-bootclasspath<路徑>用於裝入引導類的路徑
-d<目錄>輸出目錄
-o<檔案>輸出檔案(只能使用-d或-o中的一個)
-jni生成JNI樣式的標頭檔案(預設)
-version輸出版本資訊
-verbose啟用詳細輸出
-force始終寫入輸出檔案
使用全限定名稱指定<類>(例
如,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的解壓目錄。

$sudo vim /etc/profile
exportPATH=~/software/android/android-sdk-linux/tools:$PATH
 exportPATH=~/software/android/android-sdk-linux/platform-tools:$PATH

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

2.3.安裝NDK

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

2)解壓ndk到工作目錄:

$tar -xvf android-ndk-r6b-linux-x86.tar.bz2
$sudo mv android-ndk-r6b /usr/local/ndk

3)設定ndk環境變數
$sudo vim /etc/profile
exportPATH=/usr/local/ndk:$PATH
編輯完畢後儲存退出,並重啟生效

4)驗證安裝

$ cd/usr/local/ndk/samples/hello-jni/
$ ndk-build
Gdbserver : [arm-linux-androideabi-4.4.3] libs/armeabi/gdbserver
Gdbsetup : libs/armeabi/gdb.setup
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檔案如下:

<?xmlversion="1.0"encoding="utf-8"?>
<resources>
<stringname="hello">HelloWorld, JNITestActivity!</string>
<stringname="app_name">JNITest</string>
<stringname="btn_show">Show</string>
</resources>

編輯res/layout/main.xml檔案如下:

<?xmlversion="1.0"encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"/>
<EditText
android:id="@+id/ed_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"/>
<Button
android:id="@+id/btn_show"
android:layout_width="109dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/btn_show"/>
</LinearLayout>

我們在主介面上添加了一個EditText控制元件和一個Button控制元件。

3)編輯JNITest.java檔案

packageorg.tonny.jni;

importandroid.app.Activity;
importandroid.os.Bundle;
importandroid.view.View;
importandroid.widget.EditText;
importandroid.widget.Button;


publicclassJNITestextendsActivity {
static{
System.loadLibrary("JNITest");
}
privatenativeString GetReply();
privateEditTextedtName;
privateButtonbtnShow;
Stringreply;
/**Called when the activity is first created. */
@Override
publicvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
reply= GetReply();
edtName= (EditText)this.findViewById(R.id.ed_name);
btnShow= (Button)this.findViewById(R.id.btn_show);
btnShow.setOnClickListener(newButton.OnClickListener() {
publicvoidonClick(View arg0) {
edtName.setText(reply);
}
});
}
}


我們看這一段程式碼:

static{
System.loadLibrary("JNITest");
}

static表示在系統第一次載入類的時候,先執行這一段程式碼,在這裡表示載入動態庫libJNITest.so檔案。

再看這一段:

privatenativeString GetReply();

native表示這個方法由原生代碼定義,需要通過jni介面呼叫本地c/c++程式碼。

publicvoidonClick(View arg0) {
edtName.setText(reply);
}

這段程式碼表示點選按鈕後,把native方法的返回的字串顯示到EditText控制元件。

4)編譯工程,生成.class檔案。

3.2.用javah工具生成符合JNI規範的c語言標頭檔案

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

$cd ~/project/Android/JNITest/bin

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

下面繼續:

$cd classes
$javah org.tonny.jni.JNITest
$ls
org org_tonny_jni_JNITest.h

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

$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檔案,編輯程式碼如下:

#include<jni.h>
#include<string.h>
#include"org_tonny_jni_JNITest.h"


JNIEXPORTjstring JNICALLJava_org_tonny_jni_JNITest_GetReply
(JNIEnv *env, jobject obj){
return(*env)->NewStringUTF(env,(char*)"Hello,JNITest");
}

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

3.4.編寫Android.mk檔案

~/project/Android/JNITest/jni目錄下新建Android.mk檔案,android可以根據這個檔案的編譯引數編譯模組。編輯Android.mk檔案如下:

LOCAL_PATH:= $(call my-dir)
include$(CLEAR_VARS)
LOCAL_MODULE := libJNITest
LOCAL_SRC_FILES:= org_tonny_jni_JNITest.c
include$(BUILD_SHARED_LIBRARY)

LOCAL_MODULE表示編譯的動態庫名稱

LOCAL_SRC_FILES 表示原始碼檔案

3.5.用ndk工具編譯並生成.so檔案

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

$cd ~/project/Android/JNITest/
$ndk-build
Invalidattribute name:
package
Install : libJNITest.so => libs/armeabi/libJNITest.so

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

3.6.在模擬器上執行

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

$cd ~/software/android/android-sdk-linux/tools
$./emulator @AVD-2.3.3-V10 -partition-size 512

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

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

$cd ~/project/Android/JNITest/libs/armeabi/
$adb remount
$adb push libJNITest.so /system/lib
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.參考文章