android平台從froyo 2.2開始支援jni單步調試

來源:互聯網
上載者:User

轉自:http://blogold.chinaunix.net/u/26691/showart.php?id=2247039

源碼:http://blogimg.chinaunix.net/blog/upfile2/100604145124.gz

 

 

 

北京理工大學  20981  陳罡
一、寫在前面的話(可以理解為廢話)
前段時間從網上看了google的io大會,親眼看到了google在it技術發展和創新領域所做的工作(在此也問候一下apple公司的美術家門,你們辛苦了)。雖然google認為未來pc上只可能存在兩種程式——browser和game,但是就目前來看,pc平台上的application還是有一定的市場的,html5的推廣和應用畢竟是需要一定時間的(第一個吃螃蟹的人,可能會吃的很好,但是如果第一個吃蘑菇的人呢?如果吃到的是毒蘑菇呢?業內都在等,在看,一旦別人吃的還行,那個時候才會蜂擁而上)。這就好像雖然google在大力鼓吹html5的各種特性,可是自己仍然在android平台上添加對flash 10.0的支援一樣(用原話說:“hey,man,people use flash!”),從這一點來看google相對更加親民一些,而apple則更加激進一些(這並不是說誰好,誰不好,偶也喜歡激進一點的風格拉,畢竟iphone顛覆了整個手機行業,一下子把ui設計的準繩拉得很高,這讓一些諸如曾經賣木頭的公司很鬱悶)。

言歸正傳,froyo新增了許多功能,也改進了很多地方,但是偶認為對開發人員來說最值得高興的事情就是android 2.2修改了dalvik的運行機制(應該說是加入了類似jit引擎的東西吧),可以讓程式運行得更快,另外就是支援jni的單步調試(看到這個特性偶的眼淚是嘩嘩的,想當年面對著jni模組輸出的密密麻麻的log偶痛苦地閉上了眼睛。。。)。其他的諸如app to sd,無線ap,exchange,push api,語音翻譯,搜尋,等等這些都是錦上添花的事情了。

從偶的觀點來看——速度才是硬道理!

要不然真是可惜了nexus one那1ghz的處理器了。

二、ndk-r4帶給我們的驚喜
別的不說啥了,把changes.txt的重要的部分給翻譯一下發出來就好了(英語好的朋友可以直接忽略)。
changes.txt的原文在android-ndk-r4/docs目錄下:
重用要的BUG修正:
(1)<fenv.h>這個標頭檔在以前版本的ndk中放置的位置不對,這將導致在一些常規的編譯過程中無法找到該檔案進而引起編譯失敗。(這個問題在ndk-r4已經修正了)

重要的變化:
(1)在win32環境下,ndk需要配合1.7或者更高版本的cygwin使用。目前,已經官方認定在cygwin 1.5的環境下,ndk有可能不會正常工作。
(2)進一步簡化編譯過程:
開發人員不再需要進入ndk的根目錄,然後運行build/host-setup.sh指令碼來初始化環境變數或者修改$NDK_ROOT/apps目錄下面的任何檔案了,取而代之的是在編譯jni模組的時候,只需要在jni目錄下面運行一個命令“ndk-build”即可自動完成編譯過程(這個指令碼甚至連子目錄下面的模組都可以自動編譯)。
ndk-build指令碼的具體使用方法,可以參見overview.txt和ndk-build.txt這兩個文檔。
注意:
出於相容性考慮,開發人員仍然可以在"$NDK_ROOT/apps/<name>"中建立工程。但是ndk的樣本工程已經從apps目錄轉移到了"samples/<name>"目錄下,而且在編譯jni模組的時候,一定要用"ndk-build"指令碼進行編譯。
(3)引入更加方便的調試手段:
從android 2.2開始(同樣適用於未來更高版本的android系統),開發人員可以使用“ndk-gdb”工具很方便地對native的模組進行調試。具體的調試方法參見“ndk-gdb.txt”文檔。(這也是偶本文要講的內容了)
(4)支援硬體的FPU:
根據ARMv7-a層級的裝置,可以指定ndk產生新的“armeabi-v7a” ABI的機器碼。
注意:
ndk在預設的配置下,仍然會產生舊的“armeabi” ABI(基於ARMv5TE指令集)的機器碼,此配置可以在目前所知的所有android的系統image下工作。(費了半天勁,其實就是說ndk預設產生的模組相容性比較好,而新引入的功能則可以經過最佳化以後效能更高,可以使用更先進的arm指令集)。

修改ndk產生機器碼的配置主要通過修改mk檔案的APP_ABI變數完成,具體的修改方法請參考application-mk.txt文檔。

(5)提供了一個小型的“cpufeatures”靜態庫,可以通過該庫在jni運行時得到當前cpu支援的特性。該庫支援從android 1.5開始所有的平台。關於該庫的更詳細的資訊,請參考cpu-features.txt文檔。

(6)支援可選的ARM Advanced SIMD (a.k.a. NEON)特性,吧啦吧啦吧啦。下面幾個cpu相關的特性,還是給對硬體最佳化感興趣的朋友們研究好了。需要提一嘴的是,samples目錄下面有一個叫做hello-neon的範例,就是示範cpu features的,感興趣的朋友們不要錯過。

(7)添加了<android/bitmap.h>標頭檔,可以提供jni模組對傳入的android.graphics.Bitmap對象的直接操作(這個功能太有用了,否則,在jni裡面操作Bitmap對象那叫一個麻煩。。。),使用方法可以參考samples目錄下的bitmap-plasma樣本程式。

另外,就是支援一些確保不會讓模組被核心執行的一些安全功能以及一些bug修正,最後再提一句就是ndk-r4已經開始支援.s結尾的純彙編代碼編譯了(之前的之能夠支援嵌入式的彙編支援),骨灰層級的玩家注意了!

最後就是一些c和c++庫的標頭檔的修改和微調,在此不再聒噪。

三、開始體驗ndk-r4
看了上面的修改變化,想必大家都開始手癢了吧?!好的,馬上就開始安裝這個ndk-r4,當然,前提是sdk一定要更新到android 2.2這個平台對應的最新版本,當然相應的adt也要升級喔!

老規矩,偶的所有介紹都是基於linux平台的。

1、下載ndk-r4
官方的當然是:developer.android.com/sdk/ndk/index.html啦,不過由於和諧的原因我們下載不了。可以通過代理的方式去下載,相信大家總會有辦法下載下來的。

2、安裝ndk-r4
其實很簡單了,只用一個命令就行:
$unzip android-ndk-r4-linux-x86.zip
解壓完畢後會看到一個叫做android-ndk-r4的目錄,這個目錄需要export到當前的PATH環境變數裡面去:
$export PATH=~/android-ndk-r4:$PATH
然後這個ndk就安裝好了。(不要問我怎麼不運行build/host-setup.sh指令碼,那東西已經在r4被淘汰掉了)

3、編譯個hello world看看
$cd ~/android-ndk-r4/samples/hello-jni/
$ndk-build
Gdbserver      : [arm-eabi-4.4.0] /home/wayne/android-ndk-r4/samples/hello-jni/libs/armeabi/gdbserver
Gdbsetup       : /home/wayne/android-ndk-r4/samples/hello-jni/libs/armeabi/gdb.setup
Gdbsetup       : + source directory /home/wayne/android-ndk-r4/samples/hello-jni/jni
Install        : libhello-jni.so => /home/wayne/android-ndk-r4/samples/hello-jni/libs/armeabi
如果看到上面的輸出,呵呵,恭喜恭喜,jni的so庫已經編譯成功了。

4、產生一個apk看看吧
這一步比較煩,要求必須安裝了ant以及最新的android sdk,並且把$ANDROID_SDK/tools和ant的bin目錄也export到PATH環境變數裡面才行。
在此偶只貼出來偶的配置:
$export PATH=/usr/local/apache-ant-1.8.1/bin:$PATH
$export PATH=/home/wayne/android-sdk-linux_86/tools:$PATH
當然了,這些對PATH的修改,可以直接寫到/etc/profile檔案中,下次一進入shell就會自動準備好,在此按下不表。

好了,開始產生apk
$cd ../
$android update project --path hello-jni
$cd hello-jni
$ant debug
Buildfile: /home/wayne/android-ndk-r4/samples/hello-jni/build.xml
    [setup] Android SDK Tools Revision 6
    [setup] Project Target: Android 2.2
    [setup] API level: 8
    [setup] WARNING: Attribute minSdkVersion in AndroidManifest.xml (3) is lower than the project target API level (8)
    [setup] Importing rules file: platforms/android-8/ant/ant_rules_r2.xml

-compile-tested-if-test:

-dirs:
     [echo] Creating output directories if needed...

-resource-src:
     [echo] Generating R.java / Manifest.java from the resources...

-aidl:
     [echo] Compiling aidl files into Java classes...

compile:
    [javac] /home/wayne/android-sdk-linux_86/platforms/android-8/ant/ant_rules_r2.xml:255: warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds
    [javac] Compiling 1 source file to /home/wayne/android-ndk-r4/samples/hello-jni/bin/classes

-dex:
     [echo] Converting compiled files and external libraries into /home/wayne/android-ndk-r4/samples/hello-jni/bin/classes.dex...

-package-resources:
     [echo] Packaging resources
 [aaptexec] Creating full resource package...

-package-debug-sign:
[apkbuilder] Creating HelloJni-debug-unaligned.apk and signing it with a debug key...
[apkbuilder] Using keystore: /home/wayne/.android/debug.keystore

debug:
     [echo] Running zip align on final apk...
     [echo] Debug Package: /home/wayne/android-ndk-r4/samples/hello-jni/bin/HelloJni-debug.apk

BUILD SUCCESSFUL
Total time: 7 seconds
這些命令運行完畢後,如果看到上面的輸出,代表產生的apk是正常的,然後就是安裝程式。

5、安裝和偵錯工具
偶是android的fans,因此,nexus one早早就刷了froyo 2.2,因此直接在手機上裝了。鑒於很多朋友沒有nexus one,偶在下面還是用emulator做示範比較好。

首先,運行android命令,建立一個2.2的emulator。(建立模擬器的方法不再聒噪,如所示)

其次,進入android-ndk-r4/samples/hello-jni/bin目錄,應該能夠找到一個apk安裝包,直接安裝即可。
$adb install ./HelloJni-debug.apk
1201 KB/s (78926 bytes in 0.064s)
        pkg: /data/local/tmp/HelloJni-debug.apk
Success
看到Success,就代表已經安裝成功了。

然後,注意,忍受了偶上面那麼多廢話的朋友們,下面就是關鍵的地方了!
$cd ~/android-ndk-r4/samples/hello-jni/
$ndk-gdb
此時,觀察模擬器的反應,hoho,是不是看到模擬器裡面的hello-jni程式自己開啟了?!

然後就是ndk-gdb自動進入了一個命令列提示符的狀態:
and track explicitly loaded dynamic code.
warning: shared library handler failed to enable breakpoint
0xafd0eb08 in ?? ()
(gdb) list
1       /*
2        * Copyright (C) 2009 The Android Open Source Project
3        *
4        * Licensed under the Apache License, Version 2.0 (the "License");
5        * you may not use this file except in compliance with the License.
6        * You may obtain a copy of the License at
7        *
8        *      http://www.apache.org/licenses/LICENSE-2.0
9        *
10       * Unless required by applicable law or agreed to in writing, software
(gdb) list
11       * distributed under the License is distributed on an "AS IS" BASIS,
12       * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13       * See the License for the specific language governing permissions and
14       * limitations under the License.
15       *
16       */
17      #include <string.h>
18      #include <stdlib.h>
19      #include <jni.h>
20
(gdb) list
21      static void upper_str(char *s, int len) {
22        int i ; 
23        for(i = 0 ; i < len ; i++) {
24          if((s[i] >='a') && (s[i] <='z')) {
25            s[i] = s[i] - 'a' + 'A' ; 
26          }
27        }
28      }
29
30      /* This is a trivial JNI example where we use a native method
(gdb)
在linux做過開發的朋友們一定對gdb的命令列模式非常熟悉吧?到這裡就一切豁然開朗了。

可能有朋友要問,怎麼有一大堆的warning,好象是說什麼符號找不到?
其實這很簡單,android 2.2版本的模擬器自然是release版啦,debug版的一定慢得要死,所以,很多函數的庫和符號是沒有的,因此,在gdb裡面根本找不到也是有情可原的,只要忽略掉這些就行了(偶是多麼容易滿足的人啊!在此讚歎一下。另外插一句,如果有人真的愛鑽牛角尖,要弄清楚這個的話,可以嘗試用android的原始碼編譯一個debug版的android,偶試過的可以調試native的程式,而且也沒有這些warning,不過又會是一個痛苦的過程)。

然後就是常規的gdb調試命令了。
list-顯示代碼
x-查看記憶體
p-查看變數
b-設定斷點
cont-程式繼續運行
s-逐步執行,但可以跟蹤進入函數
n-逐步執行,但不會進入函數
吧啦吧啦吧啦,關於gdb的使用,偶就不再聒噪了,網上相關說明非常多。

這裡需要提醒一下,相信細心的朋友已經看到怎麼偶的介面跟大家的hello-jni不一樣?!

android的線上調試的基本原理並不是本地的gdb程式,而是在模擬器(或者真機)上啟動了一個叫做gdb-server的東西,通過adb的tcp中轉,讓開發人員原生gdb與模擬器(或者真機)上的gdb-server進行串連,然後進行調試。這個過程相對比較繁瑣,感興趣的朋友可以參考ndk-gdb這個指令碼(hoho,想當年偶調試native程式的時候,是自己寫的串連gdb和gdb-server的指令碼,期間問候了無數次google的祖宗多輩,在此按下不表)

在hello-jni裡面,jni函數的調用是放在activity的oncreate函數中的,也就是說一程式一啟動就已經調用了jni,而在pc端的gdb聯上模擬器的gdb-server的時候,jni已經運行完了,而且oncreate函數也不是隨便說調用就調用的,因此,在什麼地方下斷點呢?!偶就想了個土法,那就是修改一下原有的hello-jni程式,在裡面添加一個小button,按下這個button的時候才會調用jni的函數。

如此一來,使用ndk-gdb的時候只是啟動了activity,並不會真的調用jni函數,當偶把函數的斷點都設定好以後,再運行“cont”命令,然後單擊一下按鈕,這個時候才會真的跑到斷點裡面來。

為了大家方便起見,偶把修改過後的hello-jni的原始碼給貼出來。
希望本文能夠對android平台開發的朋友們有所協助。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.