旧文新发,看了自己的笔记应用,这是 18 年写的了,感觉应该还挺有用,分享一下吧

ABI

不同 Android 手机使用不同的 CPU,因此支持不同的指令集。CPU 与指令集的每种组合都有其自己的应用二进制界面(或 ABI)。 ABI 可以非常精确地定义应用的机器代码在运行时如何与系统交互。 您必须为应用要使用的每个 CPU 架构指定 ABI。

Android 上支持的 ABI

存放位置

Android Studio 中,应该将 so 文件按照 ABI 分类并放置在 jniLibs 文件夹下

1
2
3
4
5
├── jniLibs
│   ├── armeabi-v7a
│   │   └── libjcore110.so
│   └── x86
│   └── libjcore110.so

查看 Android 设备支持的 ABI 类型

一般来说,设备 ABI 都是固定的,这是系统在编译时决定的,在 /system/build.prop 指定了设备的 ABI 类型

primary ABI(主ABI):对应当前系统中使用的机器码类型
secondary ABI(副ABI):表示当前系统支持的其他ABI类型

比如 Nexus 5 的 build.prop 文件中是这样的

1
2
3
4
5
ro.product.cpu.abi=armeabi-v7a
ro.product.cpu.abi2=armeabi
ro.product.cpu.abilist=armeabi-v7a,armeabi
ro.product.cpu.abilist32=armeabi-v7a,armeabi
ro.product.cpu.abilist64=

可以使用 adb 命令查看

1
2
adb shell
getprop | grep abilist

可以查看当前设备支持的 ABI 类型。
例如 Nexus 5 所支持的 ABI 类型

1
2
3
[ro.product.cpu.abilist]: [armeabi-v7a,armeabi]
[ro.product.cpu.abilist32]: [armeabi-v7a,armeabi]
[ro.product.cpu.abilist64]: []

或者在 Java 代码中,使用下面的代码获取

1
2
3
Build.CPU_ABI//String 类型 primary ABI
Build.CPU_ABI2//String 类型 secondary ABI
Build.SUPPORTED_ABIS//String[] 类型 支持的 ABI 列表

可以看到该设备(Nexus 5)的主 ABI 是 armeabi-v7a,副 ABI 是 armeabi

apk 安装过程

apk 在安装的时候,Package Manager 会扫描 apk 文件,寻找符合条件的 so 库。
现根据当前设备的 primary-abi 值,寻找对应的 so 文件,当不存在 primary 的 so 库时,会寻找 secondary 的 so 库。

即 lib/{primary-abi}/libName.so
或者 lib/{secondary-abi}/libName.so

即当安装应用时,系统会根据当前设备的 CPU 架构寻找最优的 ABI 适配,如果找到合适的 so 文件,则会将整个 abi 文件夹下的 so 文件复制到 /data/data/{package.name}/lib 目录下。

注意:apk安装过程对so选择是基于整个ABI文件夹的,而非以单个so文件为粒度,也就是说把lib/armeabi 、lib/armeabi-v7a、lib/x86等等文件夹的其中一个文件夹内所有.so复制到应用的data目录下。

经验

在我的一个 app 中,由于使用了某个第三方的 SDK ,这个第三方 SDK 只提供了 armeabi-v7a 的 so 库,并且我在这个项目中还引用了 React Native ,React Native 中包含了 x86 和 armeabi-v7a 的 so 库。
所以当我在 Pad 上安装完后,打开应用后就奔溃了,奔溃日志如下:

1
com.facebook.soloader.SoLoader$WrongAbiError: APK was built for a different platform

使用 Native Libs Monitor 这个软件查看该 app 安装的 so 库时发现,该 app 使用的全是 armeabi-v7a 的 so 文件,所以导致了Crash。

解决方案

  • 方案一:
1
补齐 x86 下的 so 文件
  • 方案二:
1
将第三方 SDK 提供的 so 文件复制一份到 x86 文件夹下,并在使用到这个 SDK 的功能时进行判断当前设备的 ABI ,如果是 x86 则提示用户该功能不可用。

配置

ndkFilter

build.gradle 中设置 ndkFilter

1
2
3
4
5
defaultConfig{
ndk {
abiFilters "armeabi-v7a","x86"
}
}

这样的配置会使得打包后的 apk 文件中只保留 armeabi-v7ax86 的文件夹。

splits

可以通过 splits 生成指定的apk文件

1
2
3
4
5
6
7
8
splits {
abi {
enable true
reset()
include 'x86', 'armeabi', 'armeabi-v7a', 'mips' //选择需要为 apk 编译的 ndk abi
universalApk false //是否打包一个包含所有 abi 的 apk 包
}
}

参考文章

谈谈 Android 的 so