- 1:Android ABI
- 1.1:支持的ABI
- 2:为特定的ABI生成代码
- 3:Android 平台上的 ABI 管理
- 3.1:应用软件包中的原生代码
- 3.2:Android平台ABI支持
- 3.3:安装时自动解压缩原生代码
在编写c/c++代码时,硬件很重要。NDK 提供各种 ABI 供我们选择,可确保针对正确的架构和 CPU 进行编译。
Android ABI
不同的 Android 设备使用不同的 CPU,而不同的 CPU 支持不同的指令集。CPU 与指令集的每种组合都有专属的应用二进制接口 (ABI)。ABI 包含以下信息:
- 可使用的 CPU 指令集(和扩展指令集)。
- 运行时内存存储和加载的字节顺序。Android 始终是 little-endian。
- 在应用和系统之间传递数据的规范(包括对齐限制),以及系统调用函数时如何使用堆栈和寄存器。
- 可执行二进制文件(例如程序和共享库)的格式,以及它们支持的内容类型。Android 始终使用 ELF。
- 如何重整 C++ 名称。
ABI 还可以指平台支持的原生 API。
支持的ABI
支持的ABI | 支持的指令集 | 备注 |
armeabi-v7a | armeabi,Thumb-2,VFPv3-D16 | 与ARMv5/v6设备不兼容 |
arm64-v8a | AArch64 | |
x86和x86_64 | x86,x86-64,MMX等 |
注意:ndk以前支持ARMv5(armeabi)以及32位和64位MIPS,但ndk r17已经不再支持。
armeabi-v7a
此 ABI 适用于基于 32 位 ARM 的 CPU。Android 变体包含 Thumb-2 和 VFP 硬件浮点指令(具体而言就是 VFPv3-D16),其中包含 16 个专用 64 位浮点寄存器。
armeabi-v7a ABI 使用 -mfloat-abi=softfp 来强制实施以下规则:虽然系统可以执行浮点代码,但编译器在调用函数时必须传递整数寄存器中的所有 float 值以及整数寄存器对中的所有 double 值。
arm64-v8a
此 ABI 适用于基于 ARMv8-A 的 CPU,支持 64 位 AArch64 架构。它包含高级 SIMD (Neon) 架构扩展指令集。
x86
此 ABI 适用于支持通常称为“x86”、“i386”或“IA-32”的指令集的 CPU。
x86_64
此 ABI 适用于支持通常称为“x86-64”的指令集的 CPU。
为特定的ABI生成代码
默认情况下,Gradle会针对所有非弃用 ABI 进行构建。要限制应用支持的 ABI 集,可以使用 abiFilters。例如,要仅针对 64 位 ABI 进行构建,可以在 build.gradle 中设置以下配置:
android {
defaultConfig {
ndk {
abiFilters 'arm64-v8a', 'x86_64'
}
}
}
构建系统的默认行为是将每个 ABI 的二进制文件包括在单个 APK内。与仅包含单个 ABI 的二进制文件的 APK 相比, 要大得多;要权衡的是兼容性更广,但 APK 更大。同时仍保持最大限度的设备兼容性,一般armeabi-v7a就支持了大部分机型。
Android 平台上的 ABI 管理
应用软件包中的原生代码
Android系统可以在 APK 中符合以下格式的文件路径上找到 NDK 生成的库:
/lib/<abi>/lib<name>.so
其中,<abi> 是上面列出的 ABI 名称之一,<name> 是定义库时使用的库名称。由于 APK 文件只是 zip 文件。如果系统在预期位置找不到原生共享库,便无法使用它们。在这种情况下,应用本身必须复制这些库,然后执行 dlopen()。
在不过滤ABI的情况下,一般会包含如下库:
/lib/armeabi/libfoo.so
/lib/armeabi-v7a/libfoo.so
/lib/arm64-v8a/libfoo.so
/lib/x86/libfoo.so
/lib/x86_64/libfoo.so
Android平台ABI支持
Android系统在运行时知道它支持某种ABI,因为构建特定的系统属性会指示:
- 设备的主要 ABI,与系统映像本身使用的机器代码对应。
- 可选)与系统映像也支持的其他 ABI 对应的辅助 ABI。
此机制确保系统在安装时从软件包提取最佳机器代码。
为实现最佳性能,应直接针对主要 ABI 进行编译。例如,基于 ARMv5TE 的典型设备只会定义主 ABI:armeabi。相反,基于 ARMv7 的典型设备将主 ABI 定义为 armeabi-v7a,并将辅助 ABI 定义为 armeabi,因为它可以运行为每个 ABI 生成的应用原生二进制文件。
64 位设备也支持其 32 位变体。以 arm64-v8a 设备为例,该设备也可以运行 armeabi 和 armeabi-v7a 代码。但注意,如果应用以 arm64-v8a 为目标,而非依赖于运行 armeabi-v7a 版应用的设备,则应用在 64 位设备上的性能要好得多。
许多基于 x86 的设备也可运行 armeabi-v7a 和 armeabi NDK 二进制文件。对于这些设备,主 ABI 将是 x86,辅助 ABI 是 armeabi-v7a。
安装时自动解压缩原生代码
安装应用时,软件包管理器服务将扫描 APK,并查找以下形式的任何共享库:
lib/<primary-abi>/lib<name>.so
如果未找到,并且已定义辅助 ABI,该服务将扫描以下形式的共享库:
lib/<secondary-abi>/lib<name>.so
找到所需的库时,软件包管理器会将它们复制到应用的原生库目录 (<nativeLibraryDir>/) 下的 /lib/lib<name>.so。
如果根本没有共享对象文件,应用也会构建并安装,但在运行时会崩溃。