一. Android ABI
不同的 Android 設(shè)備使用不同的 CPU,而不同的 CPU 支持不同的指令集家破。CPU 與指令集的每種組合都有專屬的應(yīng)用二進制接口 Application Binary Interface ( ABI )抢呆,因此使用 NDK 生成在 Android 運行 .a 或 .so (都是二進制文件)就需要指定 ABI 叫榕。 目前 NDK 支持的 ABI 如下:
ABI | 支持的指令集 | CPU 架構(gòu) | 應(yīng)用 |
---|---|---|---|
armeabi-v7a | armeabi 等 | ARM 32 位 | 手機 |
arm64-v8a | AArch64 等 | ARM 64 位 | 手機 |
x86 | x86 (IA-32) 等 | x86 32 位 | PC |
x86_64 | x86-64 等 | x86 64 位 | PC |
NDK 17 前支持 ARMv5 (armeabi) 以及 32 位和 64 位 MIPS混巧,但 NDK r17 和 17 后不再支持细睡。
1. 指定 ABI
Gradle
默認情況下,Gradle(無論是通過 Android Studio 使用忍宋,還是從命令行使用)會針對所有非棄用 ABI 進行構(gòu)建瞄桨。要限制應(yīng)用支持的 ABI 集,可以使用 abiFilters讶踪。例如芯侥,要僅針對 64 位 ABI 進行構(gòu)建,可以在 build.gradle 中設(shè)置以下配置:
android {
defaultConfig {
ndk {
abiFilters 'arm64-v8a', 'x86_64'
}
}
}
ndk-build
默認情況下乳讥,ndk-build 會針對所有非棄用 ABI 進行構(gòu)建柱查。可以通過在 Application.mk 文件中設(shè)置 APP_ABI 來定位特定 ABI云石。如下:
APP_ABI := arm64-v8a # Target only arm64-v8a
APP_ABI := all # Target all ABIs, including those that are deprecated.
APP_ABI := armeabi-v7a x86_64 # Target only armeabi-v7a and x86_64.
CMAKE
使用 CMake 時唉工,一次只能針對一個 ABI 進行構(gòu)建,并且必須明確指定 ABI汹忠。為此淋硝,如果需要使用 ANDROID_ABI ,則必須在命令行中指定(無法在 CMakeLists.txt 中設(shè)置)宽菜。例如:
$ cmake -DANDROID_ABI=arm64-v8a ...
$ cmake -DANDROID_ABI=armeabi-v7a ...
$ cmake -DANDROID_ABI=x86 ...
$ cmake -DANDROID_ABI=x86_64 ...
2. 在C/C++處理 CPU 功能區(qū)別 ABI
通常谣膳,在構(gòu)建時使用#ifdef 及以下各項確定 ABI 最為方便:
- 對于 32 位 ARM ,使用 _ _ arm _ _
- 對于 64 位 ARM,使用 _ _ aarch64 _ _
- 對于 32 位 X86铅乡,使用 _ _ i386 _ _
- 對于 64 位 X86继谚,使用 _ _ x86_64 _ _
二. 使用 NDK 構(gòu)建項目
使用 NDK 編譯代碼主要有三種方式:
- 基于 Make 的 ndk-build。這種方式一般是配合 Android.mk 和 Application.mk 文件 是之前 Android Studio 使用的方式
- CMake 阵幸。這種方式基于 CMakeLists.txt 文件花履,是現(xiàn)在 Android Studio 默認使用的方式
- 獨立工具鏈 toolchain。這種方式常用于交叉編譯挚赊,常配合含有 configure 文件的項目的使用诡壁,例如 ffmpeg 編譯 so
1. NDK 主要目錄
NDK 編譯主要與下面幾個目錄有關(guān):
platforms
包含不同的 Android 版本目錄,每個 Android 版本目錄又包含 不同的 ABI 目錄荠割,在 ABI 目錄里面有 /usr/lib 目錄妹卿,包含常用的 .a 或 .so 庫,在 /usr/include 里包含需要的頭文件( ndk 16 后不保留頭文件)涨共。
sysroot
包含需要的 .a 或 .so 庫和頭文件纽帖,在 usr/lib 和 usr/include 和目錄下(ndk 16 后兩者都有,16和16前只有頭文件)举反。
toolchain
包含編譯(交叉編譯)工具鏈的目錄
build/tools
包含獨立編譯工具鏈腳本 make_standalone_toolchain.py 或者 make_standalone_toolchain.sh 懊直,其作用是根據(jù) Android API 和 ABI 集成目標系統(tǒng)庫,目標系統(tǒng)頭文件和編譯器在指定的一個目錄火鼻。當(dāng)然使用這個腳本也是可以的室囊,不過需要指定各個庫雕崩、頭文件的目錄,通常這些都不再一個目錄融撞,比較麻煩盼铁。使用 make_standalone_toolchain 的方式是:
--arch 指定 cpu 架構(gòu) --api 指定系統(tǒng)版本 --install-dir 生成的編譯工具鏈目錄
例如
./make_standalone_toolchain.py \
--arch arm --api 21 --install-dir /tmp/android-21-toolchain
在 NDK 19 開始就不需要使用獨立工具鏈了, 在 toolchains/llvm/ 下已經(jīng)提供好了編譯工具, 讀者自行去下載進入目錄看看,和獨立工具鏈編譯出來的結(jié)構(gòu)非常類似尝偎,不過它比較全的是針對不同平臺版本饶火。
2. 用獨立工具鏈編譯代碼
用獨立工具鏈編譯代碼的時需要指定下面幾個地方
- 目標處理器架構(gòu),可以用 --arch 標記來完成
- 目標系統(tǒng)頭文件(與 Android API 有關(guān))
- 目標系統(tǒng)庫 (與 Android API 有關(guān))
- 編譯器 gcc 還是 clang
不同的 NDK 版本差異比較大致扯,因此有時候同一個 configure肤寝,替換不同的 NDK 版本就不能編譯,這其實可能是 NDK 差異導(dǎo)致的抖僵。一般的差異主要是 - 目標系統(tǒng)庫和頭文件的目錄位置變化
- 是否使用獨立編譯工具鏈腳本 make_standalone_toolchain.py 或者是 make_standalone_toolchain.sh 生成的目錄來指定庫和頭文件
- ndk 編譯器 gcc or clang 的指定
3. 指定 NDK 提供的頭文件和庫
- --sysroot=XX
指定頭文件和庫鲤看,會到 XX/usr/include 目錄下查找頭文件和 XX/usr/lib 目錄下查找 .a 或者 .so 庫 - -isysroot YY
指定頭文件,覆蓋--sysroot的設(shè)置耍群,會到 YY/usr/include 目錄下查找頭文件 - -isystem ZZ
指定頭文件义桂,作補充,不覆蓋--sysroot的設(shè)置蹈垢,會到 ZZ (全路徑) 下查找頭文件 - -I XX
指定頭文件慷吊,優(yōu)先級高,不覆蓋--sysroot的設(shè)置耘婚,回到 XX (全路徑) 下查找頭文件 - -LXX
指定庫罢浇,會到 XX 目錄下查找?guī)?/li> - -lxx.xo
指定庫陆赋,直接指定某個 so 庫沐祷。
查找優(yōu)先級 -I > isystem > isysroot