我們知道,android系統的底層庫由c/c++編寫,上層android應用程式通過java虛拟機調用底層接口,銜接底層c/c++庫與java應用程式間的接口正是jni(javanative
interface)。本文描述了如何在ubuntu下配置androidjni的開發環境,以及如何編寫一個簡單的c函數庫和jni接口,并通過編寫java程式調用這些接口,最終運作在模拟器上的過程。
[html] view
plaincopy
$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)。
[plain] view
exportpath=~/software/android/android-sdk-linux/tools:$path
exportpath=~/software/android/android-sdk-linux/platform-tools:$path
編輯完畢後退出,并重新開機生效。
$tar -xvf android-ndk-r6b-linux-x86.tar.bz2
$sudo mv android-ndk-r6b /usr/local/ndk
(3)設定ndk環境變量
exportpath=/usr/local/ndk:$path
編輯完畢後儲存退出,并重新開機生效
$ 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
我們需要定義一個符合jni接口規範的c/c++接口,這個接口不用太複雜,例如輸出一個字元串。接下來,則需要把c/c++接口的代碼檔案編譯成共享庫(動态庫).so檔案,并放到模拟器的相關目錄下。最後,啟動java應用程式,就可以看到最終效果了。
(1)啟動eclipse,建立android工程
project:jnitest
package:org.tonny.jni
activity:jnitest
(2)編輯資源檔案
編輯res/values/strings.xml檔案如下:
<?xmlversionxmlversion="1.0"encoding="utf-8"?>
<resources>
<stringnamestringname="hello">helloworld, jnitestactivity!</string>
<stringnamestringname="app_name">jnitest</string>
<stringnamestringname="btn_show">show</string>
</resources>
編輯res/layout/main.xml檔案如下:
<linearlayoutxmlns:androidlinearlayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<textview
android:layout_height="wrap_content"
android:text="@string/hello"/>
<edittext
android:id="@+id/ed_name"
android:layout_width="match_parent"
android:layout_gravity="center_horizontal"
android:layout_marginleft="5dp"
android:layout_marginright="5dp"/>
<button
android:id="@+id/btn_show"
android:layout_width="109dp"
android:text="@string/btn_show"/>
</linearlayout>
我們在主界面上添加了一個edittext控件和一個button控件。
(3)編輯jnitest.java檔案
[java] view
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表示在系統第一次加載類的時候,先執行這一段代碼,在這裡表示加載動态庫libjnitest.so檔案。
再看這一段:
native表示這個方法由本地代碼定義,需要通過jni接口調用本地c/c++代碼。
這段代碼表示點選按鈕後,把native方法的傳回的字元串顯示到edittext控件。
(4)編譯工程,生成.class檔案。
在終端中,進入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 參數表示裝載類的目錄。
生成org_tonny_jni_jnitest.h頭檔案後,我們就可以編寫相應的函數代碼了。下面在android工程目錄下建立jni目錄,即~/project/android/jnitest/jni,把org_tonny_jni_jnitest.h頭檔案拷貝到jni目錄下,并在jni目錄下建立org_tonny_jni_jnitest.c檔案,編輯代碼如下:
[cpp] view
#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"
在~/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 表示源代碼檔案
進入到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檔案。
(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,現在大功告成!
androidjni開發入門之一
androidjni代碼示例講解