本文概述了如何為 x86 平台測試和調試基於 NDK 的應用。 本文將從頭至尾完整展示如何建立樣本應用,並示範如何測試和調試。
1. 開發環境
請確保已安裝了 Android 應用開發環境。 如果尚未安裝,請查看 Android 開發人員網站上的說明[1]。 在下文中,我們僅列出了所需的主要組件。
- Android SDK: Android 軟體開發套件可提供必要的工具和庫,協助開發在基於 Android 的裝置上啟動並執行應用。
- Android NDK: Android NDK 是一款支援您在 Android 應用中嵌入使用原生代碼的組件的工具套件。
在該樣本中,我們僅需要上述兩個組件,但是如果您想要使用 IDE,則需要下載並安裝下列組件:
Eclipse*:多重專案中的常用 IDE,包括通過安裝 ADT 外掛程式獲得的 Android 項目。
ADT: Android 開發工具套件,面向 Eclipse 的外掛程式。
CDT: C/C++ 開發工具套件 — 如果您希望在 Eclipse 中構建 C/C++ 代碼。
Cygwin*: Cygwin 是可為 Windows* 提供 Linux* 介面(look-and-feel)環境的工具集。 在 Windows OS 中,我們需要用它運行“ndk-build”來構建原生代碼。
2. 測試環境
對於 x86 平台目標,需要使用基於 x86 的裝置或 x86 模擬器進行測試。 隨著今年四月 IA 手機的上市,如 Lava Xolo X900 和 Lenovo K800,採用 Android 的 x86 平板電腦和手機將越來越普遍。 對於沒有 x86 裝置需要測試的使用者,使用 x86 模擬器即可。 Android SDK 管理器可用於安裝 x86 模擬器,且面向英特爾淩動平台的 Android 4.0.3 和 Android 2.3.3 映像已在官方軟體版本中提供。 請見圖 1 和 2。
圖 1. 面向 Android* 4.0.3 的英特爾 淩動? x86 系統映像
圖 2. 面向 Android* 2.3.3 的英特爾 淩動? x86 系統映像
對於無法在官方版本中找到的 Android 版本,請嘗試從 Android 原始碼構建並將其放入相應的檔案夾下(例如對於 Android 2.3.3,放在 $ANDROID_NDK_ROOT/platforms/android-10/images/ 下)。
英特爾 硬體加速執行管理器(英特爾 HAXM)也可協助開發人員獲得更快的 Android 類比速度。 HAXM 是一個硬體輔助虛擬化引擎(管理程式),該引擎可以使用英特爾 虛擬化技術 (IntelVT) 加快主機上的 Android 應用類比速度。 配合使用英特爾提供的 Android x86 模擬器映像和正式版的 Android SDK 管理器,HAXM 可以協助您在英特爾 VT 支援的系統上更快地進行類比。
圖 3.英特爾 硬體加速執行管理器
3. 編寫範例程式碼
現在,我們將建立一個名為 CPUIdApp [3] 的簡單應用,它可以返回主機 CPU 的主要特性。 原生代碼有兩種主要功能:一種是擷取 CPU 資訊;另一種是分析字串。 您可以將其寫入 cpuid.c 檔案,在 Linux 作業系統中使用 gcc 構建,或在 Windows 作業系統中使用 Cygwin 構建。 運行樣本應用後,解析的 CPU ID 資訊將會出現在螢幕上。 我們的目的是在樣本活動中展示解析的 CPU ID 資訊。 關於如何建立該項目的更多詳細資料,請參閱 [1, 4]。
第一步是建立一個 Android 項目。 請從附件的 cupid.c 中查看原始碼。
$mkdir jni-cpuid && cd jni-cpuid
該命令將建立一個簡單的“Hello World”Android 項目。
$android create project –package “com.test.cpuid� –activity “CPUIdApp� –path . –target “android-9�
為了實現我們的目標,我們需要將 src/com/test/cupid/CPUIdApp.java 更改為下列內容:
…… public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);TextView tv = new TextView(this); //create a TextView as a container for the resulttv.setText(cpuid_get()); //get parsed CPU ID information from native function and set to tvsetContentView(tv); } private static native String cpuid_get(); //declare a native function but do not implementstatic { System.loadLibrary("cpuid"); } //load the native library contains the native function we need}
下一步是在原生代碼中實現 cupid_get 功能。
$ant debug
$javah –d jni –classpath bin/classes com.test.cpuid.CPUIdApp
然後系統將自動建立包含 com_test_cpuid_CPUIdApp.h 的 jni 目錄,並在標頭檔中聲明需要實現的功能。
JNIEXPORT jstring JNICALL Java_com_test_cpuid_CPUIdApp_cpuid_1get (JNIEnv *, jclass);
然後,將 cupid.c 移至 jni 並建立一個 com_test_cpuid_CPUIdApp.c 檔案來實現 Java_com_test_cpuid_CPUIdApp_cpuid_1get 功能。
#include "com_test_cpuid_CPUIdApp.h"extern void cpuid_parse(char *buf); JNIEXPORT jstring JNICALL Java_com_example_cpuid_CPUIdApp_cpuid_1get(JNIEnv *env, jclass jc){ char buf[1024]; cpuid_parse(buf); return (*env)->NewStringUTF(env, buf);}The last step is to create Android.mk file for building the native code in the next session.LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := cpuidLOCAL_SRC_FILES := cpuid.c com_test_cpuid_CPUIdApp.cinclude $(BUILD_SHARED_LIBRARY)
4. 在 x86 模擬器上構建、部署和測試
現在開始運行 “ndk-build”來構建原生代碼吧。 如要為 x86 架構構建,需要將 “APP_ABI=x86”添加為參數或 Android.mk 檔案上。
$ndk-build APP_ABI=x86
Compile x86 : cpuid <= cpuid.cCompile x86 : cpuid <= com_test_cpuid_CPUIdApp.cSharedLibrary : libcpuid.soInstall : libcpuid.so => libs/x86/libcpuid.so
然後使用 “ant” 構建整個項目。 請在構建項目前先清理項目。
$ant clean && ant debug
CPUIdApp-debug.apk 將在“bin”目錄中建立並可通過“adb install”命令進行安裝。
$adb install bin/CPUIdApp-debug.apk
最後,在模擬器中運行 CPUIdApp 來查看輸出。
5. 調試以檢查錯誤
有兩種方式可以調試 Android NDK 應用: adb logcat and ndk-gdb. 我們將在本部分討論這兩種方法。
5.1 使用 adb logcat 擷取日誌輸出
對於基於 Java* 的應用,如果使用了 Log.d(),可以通過運行“adb logcat”來擷取日誌資訊。 運行 “adb logcat”命令時,也會顯示其它應用和系統的日誌。
運行我們之前在模擬器上建立的 CPUIdApp 後,將會出現一個錯誤對話方塊,應用將崩潰。 使用“adb logcat”檢查出現了什麼問題。
$adb logcat
…D/dalvikvm( 1616): No JNI_OnLoad found in /data/data/com.test.cpuid/lib/libcpuid.so 0xb49300f8, skipping initW/dalvikvm( 1616): No implementation found for native Lcom/test/cpuid/CPUIdApp;.cpuid_get ()Ljava/lang/String;…
日誌表示 Lcom/test/cupid/CPUIdApp.cpuid_get 功能未實現。 在 jni/com_test_cpuid_CPUIdApp.c 中檢查代碼後,我們發現 Java_com_example_cpuid_CPUIdApp_cpuid_1get 應命名為 Java_com_test_cpuid_CPUIdApp_cpuid_1get。 請注意, Typos 是開發中經常會出現的一個問題。 因此,需要在 jni/com_test_cpuid_CPUIdApp.c 中將 Java_com_example_cpuid_CPUIdApp_cpuid_1get 重新命名為 Java_com_test_cpuid_CPUIdApp_cpuid_1get 並重新構建該項目。
$ndk-build APP_ABI=x86
$ant clean && ant debug
$adb install –r bin/CPUIdApp-debug.apk
然後,在模擬器中重新運行 CPUIdApp。 正常輸出如所示。 如果您希望瞭解這些 CPUID 作業碼的具體資訊,請參閱 [4, 10]。
圖 4. CPUIdApp 結果
如欲瞭解其他資訊,可使用 android.util.Log 提供的一系列 Log.*() 功能來列印所需資訊。 這在 Java 檔案中非常簡單:
…import android.util.Log;…Log.d(“CPUIdApp”, “try to get cupid_get…”); //print useful information as needed…
重新構建並運行 CPUIdApp 後,同樣使用“adb logcat”擷取日誌輸出:
D/CPUIdApp( 2327): try to get cpuid_get...
事實上,它不僅能夠在 Java 代碼中使用,也能夠在 C/C++ 代碼中使用。 按照下列方式修改 com_test_cpuid_CPUIdApp.c:
…#include <android> #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, “CPUIdApp”, __VA_ARGS__)) … JNIEXPORT jstring JNICALL Java_com_test_cpuid_CPUIdApp_cpuid_1get(JNIEnv *env, jclass jc) { char buf[1024]; cpuid_parse(buf); LOGI("cpuid_1get buf value: "); //the same way in native side to print useful information LOGI(buf); return (*env)->NewStringUTF(env, buf); } To build this, we need to modify Android.mk, by adding: LOCAL_LDLIBS := -llog LOCAL_C_INCLUDES += system/core/include/cutils LOCAL_SHARED_LIBRARIES := libcutils </android>
然後:
$ndk-build APP_ABI=x86 && ant clean && ant debug && adb install –r bin/CPUIdApp-debug.apk
運行後您將會獲得下列日誌資訊:
I/CPUIdApp( 2475): cpuid_1get buf value:
I/CPUIdApp( 2475): CPUID Features(EAX=1): ECX = 0x00000201 EDX = 0x0781abfd
I/CPUIdApp( 2475): HT - NO
I/CPUIdApp( 2475): VT - NO
I/CPUIdApp( 2475): SSE - YES
I/CPUIdApp( 2475): SSE2 - YES
I/CPUIdApp( 2475): SSE3 - YES
I/CPUIdApp( 2475): SSSE3 - YES
I/CPUIdApp( 2475): SSE41 - NO
I/CPUIdApp( 2475): SSE42 - NO
I/CPUIdApp( 2475): AES - NO
I/CPUIdApp( 2475): AVX - NO
5.2 使用 ndk-gdb 調試 C/C++ 代碼
Android NDK r4 採用了一個名為 “ndk-gdb”的輔助 shell 指令碼,以便能夠在 NDK 產生的機器代碼中輕鬆啟動原生偵錯工作階段。 指令碼位於 NDK 的頂級目錄,可通過應用項目目錄或其他子目錄中的命令列來調用。
我們試一下:
在項目目錄的根目錄中,運行“ndk-gdb --start”,以下的輸出將會出現在終端上:
發現的可調試標記: false
錯誤: 軟體包 com.test.cpuid 不可調試! 您可以通過以下兩種方式進行修複:
— 調用“ndk-build”時,使用 NDK_DEBUG=1 重新構建。
— 修改您的清單以設定 android:將可調試屬性設定為“true”,然後按照正常步驟重新構建。
使用上述兩種方式: 1)修改清單,並在應用標籤中添加 android:debuggable=”true” ;並 2) 通過運行 “ndk-build APP_ABI=x86 NDK_DEBUG=1” 進行重新構建。
$ ndk-build APP_ABI=x86 NDK_DEBUG=1
$ ant clean && ant debug && adb install –r bin/CPUIdApp-debug.apk
$ndk-gdb –start com.test.cpuid.CPUIdApp –force –verbose
應用被啟動,並將顯示:
……and track explicitly loaded dynamic code.0xb7eb2ff9 in __futex_syscall4 () from /home/user/labs/cpuid/obj/local/x86/libc.so(gdb)
所有“gdb”命令均可用於調試原生代碼。 有時,在我們看到 “gdb”提示前,原生部分已完成。 此時,在 src/com/test/cpuid/CPUIdApp.java 處添加 “android.os.Debug.waitForDebugger()”:
…import android.os.Debug;… Log.d("CPUIdApp", "waiting for debug ..."); android.os.Debug.waitForDebugger(); //make app pause Log.d("CPUIdApp", "try to get cpuid_get..."); tv.setText(cpuid_get());…
通過這種方式,我們可以對 cupid_get() 功能進行調試並獲得更多詳細資料。
6. 結論
在本文中,我們藉助簡單的樣本應用介紹了基於 Android NDK 的應用的建立、安裝、測試和調試。您可以在真正的 x86 裝置或 x86 模擬器上,並通過“adb logcat”和 Android SDK/NDK 工具中提供的“ndk-gdb”命令來測試應用。 這些裝置可在真正的 x86 裝置或 x86 模擬器上測試,並可通過 “adb logcat”(由 Android SDK/NDK 工具提供)進行調試。
使用“adb logcat”擷取簡單資訊或與系統相關的資訊。
如欲瞭解詳細資料,需要使用“ndk-gdb”。
我們僅介紹了如何通過命令列執行這些步驟,對於想要使用 Eclipse 的開發人員,請參閱 [7 和 8]。
參考:
[1] Android 開發人員網站。 http://developer.android.com/index.html
[2] 樣本 NDK 應用。 http://developer.android.com/sdk/ndk/overview.html#samples=
[3] 英特爾硬體加速執行管理器安裝指南。 http= ://redfort-software.intel.com/en-us/articles/installation-instructions-fo= r-intel-hardware-accelerated-execution-manager-windows/
[4] Wiki 上有關 CPUID opcode 的資訊。 http://en.wikipedia.org/wiki/CPUID
[5] 面向英特爾架構的基於 NDK 的 Android 應用。 http://redfort-software.intel.com/en= -us/articles/creating-and-porting-ndk-based-android-apps-for-ia/
[6] 藉助 ndk-debug 使用 cgdb。 http://mhandroid.wordpress.com/2011/01/23/using-cgdb-with-= ndk-debug-and-cgdb-tutorial/
[7] 使用用於 Android C/C++ 調試的 Eclipse。 http://mhandroid.wordpress.com/2011/01/23/using-eclipse-fo= r-android-cc-debugging/
[8] 使用用於 Android C/C++ 開發的 Eclipse。 http://mhandroid.wordpress.com/2011/01/23/using-eclipse-fo= r-android-cc-development/
[9] 面向英特爾架構的 Android NDK。 htt= p://redfort-software.intel.com/en-us/articles/ndk-for-ia/
[10] 英特爾 64 和 IA-32 架構開發人員手冊: Vol.2A. http://ww= w.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-archit= ectures-software-developer-vol-2a-manual.html
[11] NDK-GDB 介紹。 http://source-android.frandroid.com/ndk/docs/NDK-GDB.html
聲明
本檔案中包含關於英特爾產品的資訊。 本檔案不構成對任何智慧財產權的授權,包括明示的、暗示的,也無論是基于禁止反言的原則或其他。 英特爾不承擔任何其他責任。英特爾在此作出免責聲明:本檔案不構成英特爾關於其產品的使用和/或銷售的任何明示或暗示的保證,包括不就其產品的(i)對某一特定用途的適用性、(ii)適銷性以及(iii)對任何專利、著作權或其他智慧財產權的侵害的承擔任何責任或作出任何擔保。
除非經過英特爾的書面同意認可,英特爾的產品無意被設計用於或被用於以下應用:即在這樣的應用中可因英特爾產品的故障而導致人身傷亡。
英特爾有權隨時更改產品的規格和描述而毋需發出通知。 設計者不應信賴任何英特產品所不具有的特性,設計者亦不應信賴任何標有“保留權利”或“未定義”的說明或特性描述。 對此,英特爾保留將來對其進行定義的權利,同時,英特爾不應為因其日後更改該等說明或特性描述而產生的衝突和不相容承擔任何責任。 此處的資訊可能隨時更改,恕不另行通知。 請勿使用本文資訊完成一項產品設計。
本檔案所描述的產品可能包含使其與宣稱的規格不符的設計缺陷或失誤。 這些缺陷或失誤已收錄於勘誤表中,可索取獲得。
在發出訂單之前,請聯絡當地的英特爾營業部或分銷商以擷取最新的產品規格。
索取本檔案中或英特爾的其他材料中提的、包含訂單號的檔案的複印件,可撥打1-800-548-4725,或登陸http://www.intel.com/design/literature.htm
在效能檢測過程中涉及的軟體及其效能只有在英特爾微處理器的架構下方能得到最佳化。 諸如 SYSmark 和 MobileMark 等測試均系基於特定電腦系統、硬體、軟體、作業系統及功能。 上述任何要素的變動都有可能導致測試結果的變化。 請參考其他資訊及效能測試(包括結合其他產品使用時的運行效能)以對目標產品進行全面評估。
對本檔案中包含的軟體原始碼的提供均依據相關軟體許可而做出,任何對該等原始碼的使用和複製均應按照相關軟體許可的條款執行。
英特爾、Atom、淩動和 Intel 標識是英特爾在美國和/或其他國家的商標。
英特爾公司 2013 年著作權。 著作權所有,並保留一切權利。
*其他的名稱和品牌可能是其他所有者的資產。