Android NDK 功能概述
Android NDK就是一套用於把C/C++源碼編譯得到的二進制機器碼嵌入應用安裝包的工具。
Android NDK是對Android SDK的一個補充,可以幫助你:
1)生成符合JNI規範的共享庫(運行在Android 1.5以上系統,主要是ARM CPU)
2)將共享庫拷貝到工程合適位置(拷貝之後,在生成apk時,該共享庫自動打包進最終的apk文件)。
3)將來的NDK版本,會提供調試本地碼的工具(利用gdb連接和大量源碼及符號信息)。
Android NDK提供了:
1)一套交叉編譯工具鏈(編譯器、鏈接器等),可在Linux, OS X和Windows(通過 Cygwin)系統上直接編譯出ARM代碼。
2)一套系統頭文件。這些頭文件對應Android平臺的穩定API(見Android Stable APIs)。所謂穩定API,就是今後更高版本的Android系統都會支持的API。
3)一個編譯系統。開發人員只需編寫很短的編譯腳本文件,描述哪些源碼文件需要編譯,如何編譯。該編譯系統能處理繁瑣的工具鏈/平臺/CPU/ABI 等細節。今後版本的NDK將支持更多工具鏈、平臺、系統接口,而不用修改編譯腳本。
注意事項:
1)Android NDK只能用於Cupcake以後(即Android 1.5 以後)。尤其是1.0和1.1版Android,NDK完全不支持(因爲ABI和工具鏈有一些修改,導致了不兼容)。
2)Android系統除穩定API,還自帶很多共享庫,但大部分都是可變的,今後有可能做修改。使用非穩定的API,在系統更新之後,程序可能無法工作。
I. Android NDK的目標
Android 虛擬機支持通過JNI調用本地碼實現的函數(本地碼就是C/C++編寫,在不同平臺上編譯得到的機器指令。對arm而言,就是arm指令;對x86而言,就是x86指令)。這意味着:
1)你在用java開發Android應用時,可以用native關鍵字,聲明相應方法是在本地碼中實現的。例如:
native byte[] loadFile(String filePath);
2)對於這些native方法,你必須提供一個共享庫(*.so文件),包含這些方法的實現。這個共享庫將來會打包進apk。這個共享庫必須按照UNIX共享庫的命名規則命名,例如:
lib<something>.so
它還必須包含一個標準的JNI入口函數。
3)java程序在使用這些native方法之前,必須加載該共享庫。例如,下面代碼是在啓動的時候加載:
static {
System.loadLibrary("FileLoader"); // 這裏應該取共享庫文件名中間部分,不含“lib”前綴和“.so”後綴。
}
II. Android NDK不能完成的事情
用NDK開發一個一般化程序不是好方法,應該儘量用Java開發,處理各種Android系統事件和Android程序生命週期。
不過,你可以利用NDK來編寫一個程序(大部分是C/C++),然後用一個小的啓動器(Java)來加載。
對JNI有一程度的瞭解,能夠幫助你更好地使用NDK。
NDK其實只提供了Android系統庫的少數有限API,更多API因爲可能修改所以沒有在NDK中提供(因此找不到相應的頭文件)。
III. NDK開發步驟
這裏初步描述一下如何使用Android NDK進行開發:
1)將源碼文件放在 $PROJECT/jni/...
2)編寫 $PROJECT/jni/Android.mk 文件(編譯腳本,供Android NDK編譯系統使用)
3)可選:編寫 $PROJECT/jni/Application.mk (供Android NDK編譯系統使用。非必須。
可以用來支持更多的CPU平臺,或用來修改編譯器參數)。
4)進入工程根目錄之後,運行ndk-build,完成編譯。編譯完成後,會自動拷貝strip過的共享庫
到工程的根目錄。然後,通過正常步驟來生成最終的apk文件。
上面步驟更詳細的描述如下:
III.1 配置 NDK: 老版本NDK需要運行 build/host-setup.sh 腳本(配置NDK)。NDK r4之後(含r4),不需要該步驟。
III.2 拷貝C/C++文件到jni目錄: 將 C/C++ 文件拷貝到 $PROJECT/jni/ 。該目錄下的文件結構任意,不影響最終的apk。
C++代碼的默認文件擴展名爲cpp,其他擴展名也可以(見 android-mk )。 也可以把源碼放到其他地方,只要在Android.mk中寫明即可。
III.3 編寫 Android.mk: 該文件有自己的語法(見android-mk)。NDK是把那些C/C++代碼文件看做不同的“模塊”,每個模塊可以是一個靜態庫,也可以是一個動態庫。在一個Android.mk文件中可以定義多個模塊。也可以爲每個模塊寫一個android.mk。
注意:同一個Android.mk可能被Android編譯系統解析多次,所以要注意前面的環境變量對後面的影響。
默認情況下,NDK會尋找 $PROJECT/jni/Android.mk 文件。如果jni目錄下還有子目錄,可以在這些子目錄中創建Android.mk,然後在最頂層的Android.mk文件中把這些Android.mk包含進來。有一個輔助函數(將把jni目錄下所有的Android.mk包含進來):
include $(call all-subdir-makefiles)
III.4 編寫 Application.mk(可選): Application.mk描述程序自身,而Android.mk描述模塊如何編譯。Application.mk提供很多功能,一些重要功能如下:
1)列出你的程序必需的模塊 2)指定CPU架構 3)可選信息:例如想要release還是debug版本,C/C++編譯器標誌等全局性信息。
該文件是可選的,如果你沒有提供,則編譯系統用默認的Application.mk(將Android.mk文件中描述的模塊全部編譯,默認CPU爲armeabi)
使用Application.mk的兩種方法:
1)放在 $PROJECT/jni/Application.mk ,會被ndk-build腳本自動檢測。
2)放在 $NDK/apps/<name>/Application.mk,進入NDK所在目錄,執行 make APP=<name> 來完成編譯。
以上第2)種方法用於NDK r4以前,現在依然支持(兼容)。 建議用方法1),因爲它更簡單,且不需要修改NDK安裝目錄。
III.5 調用NDK編譯系統
調用的方式有2種:1)直接執行 ndk-build (更好) 2)在 $NDK/apps 下新建一個子目錄的方法(老方法)
這兩種方法編譯成功後,將把strip過的二進制模塊(即共享庫)拷貝到工程目錄(沒有strip的模塊也會保留,用於調試)。
1)用 ndk-build 命令
ndk-build腳本位於NDK的根目錄,可以在進入工程目錄之後調用(工程目錄就是你的Android工程中,AndroidManifest.xml文件所在的目錄)。
例如:
$cd /home/wuxiao/workspace/NotePad (NotePad 是用eclipse創建的)
$ndk-build
$ndk-build clean 相當於 make clean
$ndk-build -B V=1 -B 是強制重新編譯, V=1 表示打印詳細信息
編譯出二進制代碼後,按普通Android的app的步驟,生成apk。此時共享庫已拷貝到工程的根目錄,
所以這個共享庫會進入apk。
2)老方法:用 $NDK/apps/<name>/Application.mk 方式(爲了兼容而保留,即將被刪除,此處省略)
IV. 編譯生成apk
用NDK生成二進制模塊後,按正常步驟編譯生成apk(可用ant或ADT,在Android SDK文檔中講解)。
生成的apk包含該共享庫。用戶在安裝該apk的時候,共享庫會被系統自動提取。
V. 調試該共享庫
NDK提供了一個ndk-gdb,運行後開啓一個調試會話,連接到你的應用程序。
這種本地碼調試只能在Android 2.2以上系統應用,不需要root權限或其他特權,只需要開啓調試。
大致步驟如下(詳細步驟見文檔 NDK-BUILD):
1)在 AndroidManifest.xml 中設置調試功能爲開啓(將 android:debuggable 設置爲 true)。
2)用 ndk-build 編譯共享庫,生成apk,安裝apk
3)啓動該程序
4)cd進入工程目錄,執行 ndk-gdb,就進入了gdb命令行。