一、ABI 是什么
ABI 是 Application Binary Interface 的缩写。
不同 Android 手机使用不同的 CPU,因此支持不同的指令集。CPU 与指令集的每种组合都有其自己的应用二进制界面(或 ABI)。 ABI 可以非常精确地定义应用的机器代码在运行时如何与系统交互。 您必须为应用要使用的每个 CPU 架构指定 ABI。
典型的 ABI 包含以下信息:
机器代码应使用的 CPU 指令集。
运行时内存存储和加载的字节顺序。
可执行二进制文件(例如程序和共享库)的格式,以及它们支持的内容类型。
用于解析内容与系统之间数据的各种约定。这些约定包括对齐限制,以及系统如何使用堆栈和在调用函数时注册。
运行时可用于机器代码的函数符号列表 - 通常来自非常具体的库集。
在 gardle 中可以通过 abiFilters 来指定我们需要的 ABI:
abiFilters "armeabi", "armeabi-v7a" , "arm64-v8a", "x86", "x86_64", "mips", "mips64"
- armeabiv-v7a: 第7代及以上的 ARM 处理器。2011年15月以后的生产的大部分Android设备都使用它.
- arm64-v8a: 第8代、64位ARM处理器,很少设备,三星 Galaxy S6是其中之一。
- armeabi: 第5代、第6代的ARM处理器,早期的手机用的比较多。
- x86: 平板、模拟器用得比较多。
- x86_64: 64位的平板。
支持的 ABI
每个 ABI 支持一个或多个指令集。表 1 概述了每个 ABI 支持的指令集。Android系统目前支持以下七种不同的CPU架构:ARMv5,ARMv7 (从2010年起),x86 (从2011年起),MIPS (从2012年起),ARMv8,MIPS64和x86_64 (从2014年起),每一种都关联着一个相应的ABI。
- 很多设备都支持多于一种的ABI。
- 当一个应用安装在设备上,只有该设备支持的CPU架构对应的.so文件会被安装
表 1. ABI 和支持的指令集。
ABI | 支持的指令集 | 备注 |
---|---|---|
armeabi |
| 在 r16 中已弃用。在 r17 中已移除。无硬浮点数。 |
armeabi-v7a |
| 与 ARMv5、ARMv6 设备不兼容。 |
arm64-v8a |
| |
x86 |
| 不支持 MOVBE 或 SSE4。 |
x86_64 |
|
注意:NDK 以前支持 32 位和 64 位 MIPS,但这项支持已在 NDK r17 中移除。
https://developer.android.com/ndk/guides/abis
可以通过Build.SUPPORTED_ABIS得到根据偏好排序的设备支持的ABI列表。但你不应该从你的应用程序中读取它,因为Android包管理器安装APK时,会自动选择APK包中为对应系统ABI预编译好的.so文件,不同的ABI,针对不同的cpu架构有不同的优先权例如: x86设备上,libs/x86目录中如果存在.so文件的话,会被安装,如果不存在,则会选择armeabi-v7a中的.so文件,如果也不存在,则选择armeabi目录中的.so文件。x86设备能够很好的运行ARM类型函数库,但并不保证100%不发生crash,特别是对旧设备。64位设备(arm64-v8a, x86_64, mips64)能够运行32位的函数库,但是以32位模式运行,在64位平台上运行32位版本的ART和Android组件,将丢失专为64位优化过的性能(ART,webview,media等等)
一、如果你的项目中,有其他优先级更高的ABI目录,但是你把ABI文件放到了优先级低的目录,则你的ABI文件无法被加载
二、如果你的项目中,ABI文件放在了,项目中优先级最高的ABI目录中(这个ABI目录是手机所支持的在项目中优先级最高的,但不一定是手机所支持的优先级最高的),则这个ABI文件,可以被加载,加载为ABI目录的所表示的架构类型。
例子:
我的手机cpu架构是ARMv7,ABI文件是armeabi-v7a,但是放进了armeabi目录中
在运行的过程中会出现两种情况:
1、项目中有armeabi-v7a的目录,则armeabi目录中的文件无法被加载,运行后报错,出现如下log信息。
Caused by: java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/.xx../base.apk"],nativeLibraryDirectories=[/data/app/.xx../lib/arm, /vendor/lib, /system/lib]]] couldn't find "lib..xx...so"
2、当项目中只有armeabi的目录时,armeabi目录是该项目优先级最高的ABI目录(虽然armeabi目录在ARMv7所支持的优先级最高的ABI目录不是最高),作为armv5,安装到手机上。
在 apk 文件中带这么多版本的 .so 是一种很不经济的做法:
- mips / mips64: 极少用于手机可以忽略
- x86 / x86_64: x86 架构的手机都会包含由 Intel 提供的称为 Houdini 的指令集动态转码工具,实现 对 arm .so 的兼容,再考虑 x86 1% 以下的市场占有率,x86 相关的两个 .so 也是可以忽略的
- armeabi: ARM v5 这是相当老旧的一个版本,缺少对浮点数计算的硬件支持,在需要大量计算时有性能瓶颈
- armeabi-v7a: ARM v7 目前主流版本
- arm64-v8a: 64位支持
这样我们就可以明确 mips, mips64, x86, x86_64 这 4 个 .so 我们是不需要的。
正确的做法
当前市面绝大多数是arm的CPU,而且都是V7架构的了,所以可以保留armeabi或者armeabi-v7a即可。
如果仅保留armeabi-v7a,而有些第三方包未提供v7a的包,则可以将对应armeabi包拷贝到armeabi-v7a。
如果同时保留armeabi和armeabi-v7a,则需要保证两个目录下的so库文件数相同。
arm64-v8a是可以向下兼容的,但前提是你的项目里面没有arm64-v8a的文件夹,如果你有两个文件夹armeabi和arm64-v8a,两个文件夹,armeabi里面有a.so 和 b.so,arm64-v8a里面只有a.so,那么arm64-v8a的手机在用到b的时候发现有arm64-v8a的文件夹,发现里面没有b.so,就报错了,所以这个时候删掉arm64-v8a文件夹,这个时候手机发现没有适配arm64-v8a,就会直接去找armeabi的so库,所以要么你别加arm64-v8a,要么armeabi里面有的so库,arm64-v8a里面也必须有.
处理.so文件时有一条简单却并不知名的重要法则。
你应该尽可能的提供专为每个ABI优化过的.so文件,但要么全部支持,要么都不支持:你不应该混合着使用。你应该为每个ABI目录提供对应的.so文件。
当一个应用安装在设备上,只有该设备支持的CPU架构对应的.so文件会被安装。在x86设备上,libs/x86目录中如果存在.so文件的话,会被安装,如果不存在,则会选择armeabi-v7a中的.so文件,如果也不存在,则选择armeabi目录中的.so文件(因为x86设备也支持armeabi-v7a和armeabi)。
64位和32位是指CPU的通用寄存器数据宽度。
操作系统设计初衷不同,64位操作系统的设计初衷是满足机械设计和分析、三维动画、视频编辑和创作,以及科学计算和高性能计算应用程序等领域中需要大量内存和浮点性能的客户需求,主要考虑的是运行一些比较特殊的行业性应用软件,而32位操作系统是为普通用户设计的,主要考虑的是运行日常的应用软件。
区别:
处理器有所不同
所谓64位处理器的“位数”,这个位数指的是CPU的GPRs(General-Purpose Registers通用寄存器)的数据宽度为64位,一次能够处理64bit的数据(32位是32bit),而寄存器是CPU中用来种暂时存放数据和地址的,是CPU构成的一部分。
软件有所区别
64位操作系统主要考虑是运行一些比较特殊的行业性应用软件,而这些软件都是采用64位计算,所以这些软件只能运行在64位操作系统下。32位应用软件主要是一些日常软件,比如QQ、MSN等,但32位应用软件也可以兼容运行在64位操作系统下,不过这也是出于过渡上的考虑。
对内存的控制不同
32位操作系统实际可识别的内存为3.5GB,而64位操作系统实际可支持128GB内存,这也是64位和32位在表面上以及实际价值上最主要的不同。
确保您的应用支持 64 位设备
从 2019 年 8 月 1 日开始,您在 Google Play 上发布的应用必须支持 64 位架构。64 位 CPU 能够为您的用户提供更快、更丰富的体验。添加 64 位的应用版本不仅可以提升性能、为未来创新创造条件,还能针对仅支持 64 位架构的设备做好准备。
本指南介绍了如何确保 32 位应用为支持 64 位设备做好准备,供您随时采用。
评估您的应用
如果您的应用仅使用以 Java 编程语言或 Kotlin 编写的代码(包括任何库或 SDK),那么就表示该应用已经支持 64 位设备。如果您的应用使用了任何原生代码,或者您不确定自己的应用是否使用了这类代码,那么您需要评估应用并相应采取措施。
您的应用是否使用了原生代码?
首先需要检查您的应用是否使用了任何原生代码。如果您的应用符合以下情况,便是使用了原生代码:
- 使用了任何 C/C++(原生)代码。
- 与任何第三方原生库关联。
- 通过使用原生库的第三方应用构建程序构建而成。
您的应用是否包含 64 位库?
要确定应用是否包含 64 位库,最简单的方法就是检查 APK 文件的结构。在编译时,APK 会与应用所需的所有原生库打包在一起。原生库会根据 ABI 而存储在不同的文件夹中。您的应用无需支持所有 64 位架构,但对于您支持的每种原生 32 位架构,则应用都必须包含相应的 64 位架构。
对于 ARM 架构,32 位库位于 armeabi-v7a 中。对应的 64 位库位于 arm64-v8a 中。
对于 x86 架构,请查找 x86(32 位)和 x86_64(64 位)。
首先要确保这两个文件夹中都有原生库。总结如下:
平台 | 32 位库文件夹 | 64 位库文件夹 |
---|---|---|
ARM | lib/armeabi-v7a | lib/arm64-v8a |
x86 | lib/x86 | lib/x86_64 |
请注意,每个文件夹中的一套库可能完全相同,也可能不完全相同,这取决于您的应用。您应达到的目标是确保您的应用能够在仅支持 64 位架构的环境中正常运行。
通常情况下,同时针对 32 位和 64 位架构编译的 APK 或软件包会具有这两种 ABI 的文件夹,每个文件夹中都有一套相应的原生库。如果您的应用不支持 64 位架构,那么您很可能会看到 32 位 ABI 文件夹,但不会看到 64 位文件夹。
使用 APK 分析器查找原生库
APK 分析器是可用来对所编译的 APK 进行各方面评估的工具。针对我们目前所讨论的情况,我们将使用该工具查找原生库,以确定是否具备 64 位库。
- 打开 Android Studio,然后打开任一项目。
- 从菜单中依次选择 Build > Analyze APK…
- 选择您要评估的 APK。
-
查看 lib 文件夹,您可以在其中找到所有“.so”文件。如果在您的应用中找不到任何“.so”文件,则说明该应用已经准备好相应的库,您无需采取进一步措施。如果您看到 armeabi-v7a 或 x86,则说明您有 32 位库。
-
检查是否在 arm64-v8a 或 x86_64 文件夹中也有类似的“.so”文件。
-
如果您没有任何 arm64-v8a 或 x86_64 库,则需要更新编译流程以开始在 APK 中编译并打包相应工件。
-
如果您看到 32 位和 64 位的库均已打包到软件包中,则可以跳至在 64 位设备上测试应用。
在解压缩的 APK 中查找原生库
APK 文件的结构类似于 zip 文件,可以像 zip 文件一样解压缩。如果您更喜欢使用命令行或任何其他解压缩工具,也可以采用解压缩 APK 的方法。
只需解压缩 APK 文件(根据您使用的解压缩工具,您可能需要将其重命名为 .zip),然后按照上文中的指南浏览解压缩后的文件,即可确定您的应用是否已经为支持 64 位设备做好准备了。
例如,您可以从命令行中运行如下命令:
:: Command Line
> zipinfo -1 YOUR_APK_FILE.apk | grep \.so$
lib/armeabi-v7a/libmain.so
lib/armeabi-v7a/libmono.so
lib/armeabi-v7a/libunity.so
lib/arm64-v8a/libmain.so
lib/arm64-v8a/libmono.so
lib/arm64-v8a/libunity.so
请注意,此示例中存在 armeabi-v7a 库和 arm64-v8a 库,这表明该应用支持 64 位架构。
使用 64 位库编译应用
下面针对编译 64 位库做出了相关的说明。不过,需要指出的是,以下内容仅介绍了如何编译在源代码的基础上可编译的代码和库。
如果您使用任何外部 SDK 或库,请确保按照上文所述的步骤使用 64 位版本。如果没有 64 位版本可用,请与相应 SDK 或库的所有者联系,并在规划支持 64 位设备的方案时将这一点考虑在内。
使用 Android Studio 或 Gradle 进行编译
大多数 Android Studio 项目都使用 Gradle 作为底层编译系统,因此本部分适用于使用这两种工具进行编译的情况。针对原生代码进行编译很简单,只需将 arm64-v8a 和/或 x86_64(取决于您要支持的架构)添加到应用的“build.gradle”文件中的 ndk.abiFilters 设置:
// Your app's build.gradle
apply plugin: 'com.android.app'
android {
compileSdkVersion 27
defaultConfig {
appId "com.google.example.64bit"
minSdkVersion 15
targetSdkVersion 28
versionCode 1
versionName "1.0"
ndk.abiFilters 'armeabi-v7a','arm64-v8a','x86','x86_64'
// ...
利用 Android App Bundle 减小大小增加量
为您的应用添加 64 位架构支持可能会导致 APK 的大小增加。我们强烈建议您利用 Android APP Bundle 功能,以尽量减小因在同一 APK 中同时包含 32 位和 64 位原生代码而对 APK 大小产生的影响。
实际上,让应用改用 Android App Bundle 不仅能够降低 APK 大小,甚至能让其变得比现在更小。
在 64 位硬件上测试应用
64 位版本的应用应提供与 32 位版本相同的质量和功能集。请对您的应用进行测试,以确保使用最新的 64 位设备的用户能够在您的应用中获得优质的体验。
要开始测试您的应用,您要有支持 64 位架构的设备。时下有很多支持 64 位架构的热门设备,例如 Google 的 Pixel 以及其他旗舰设备。
要测试您的 APK,最简单的方法就是使用 adb 安装该应用。大多数情况下,您可以提供 --abi
参数,用以指示要将哪些库安装到设备上。这样在设备上安装该应用时便会仅包含 64 位库。
:: Command Line
# A successful install:
> adb install --abi armeabi-v7a YOUR_APK_FILE.apk
Success
# If your APK does not have the 64-bit libraries:
> adb install --abi arm64-v8a YOUR_APK_FILE.apk
adb: failed to install YOUR_APK_FILE.apk: Failure [INSTALL_FAILED_NO_MATCHING_ABIS: Failed to extract native libraries, res=-113]
# If your device does not support 64-bit, an emulator, for example:
> adb install --abi arm64-v8a YOUR_APK_FILE.apk
ABI arm64-v8a not supported on this device
https://developer.android.com/distribute/best-practices/develop/64-bit
安装成功后,请照常对应用进行测试,以确保其质量与 32 位版本相同。
放置 .so 文件的正确姿势其实就两句话:
- 为了减小 apk 体积,只保留 armeabi 和 armeabi-v7a 两个文件夹,并保证这两个文件夹中 .so 数量一致
- 对只提供 armeabi 版本的第三方 .so,原样复制一份到 armeabi-v7a 文件夹
https://zhuanlan.zhihu.com/p/21359984