那些和 so 库有关的问题

旧文新发,看了自己的笔记应用,这是 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

作者

PPTing

发布于

2020-04-12

更新于

2022-02-12

许可协议

评论