Android:如何正确放置so库文件
问题描述
当需要引用 so 库的时候,正常情况下我们只需要将不同版本的 so 文件分别放置。但是这样就会遇到两个问题:
- 如果每个架构的 so 库都放进去,会大大增加 apk 包的大小
- 如果偏偏没有 arm-v7a 的版本呢?是删除 armeabi-v7a 目录只保留 armeabi ?还是说两个目录下 so 文件数不同也没有关系?
首先了解一下 so 文件的类型
so 文件的类型
在 Android 系统中 ndk 默认会生成如下 7 种 .so。
- armeabi ARM:v5 第5代、第6代的ARM处理器,早期的手机用的比较多。缺少对浮点数计算的硬件支持,在需要大量计算时有性能瓶颈
- armeabi-v7a:v7 目前主流版本,2011年15月以后的生产的大部分Android设备都使用它
- arm64-v8a:第8代、64位ARM处理器,很少设备,三星 Galaxy S6是其中之一
- x86:平板、模拟器用得比较多
- x86_64:x86架构的64位。
- mips:极少用于手机
- mips64:mips架构的64位
首先mips和mips64极少用于手机,因此可以忽略;x86和x86_64的架构都会包含由 Intel 提供的称为 Houdini 的指令集动态转码工具,实现 对 arm 的兼容,再考虑 x86 1% 以下的市场占有率,x86 相关的两个 so 也是可以忽略的;arm64-v8a 是可以向下兼容的,因此也是可以忽略的。
也就是说,项目中只需要保存两种架构即可:armeabi 和 armeabi-v7a。
android加载 so 库的模式
比如当前用户的手机是 armeabi-v7a 的,apk的armeabi包下有 a.so 和 b.so 两个库,而 armeabi-v7a 包下只有 a.so 一个库。
- android4.4 的so文件拷贝逻辑
android在扫描apk文件的时候,并没有保证 zip entry 的扫描顺序。加载 libs/<abi>/libxxx.so 时,首先判断这个 so 库是否是当前CPU的架构,如果是的话,则将一个类似名为 hasPrimary 的 boolean 值置为 true ,然后将 so 库拷贝到手机上;如果不是的话,则判断是否已经加载过当前CPU架构的 so 库了,即判断 hasPrimary 的值,只有 hasPrimary 的值为 false 时,即还没有加载过当前CPU架构的 so 库,则将这个 so 库拷贝到手机上。
- 如果首先扫描到 armeabi 包下的 a.so 库,此时因为 hasPrimary 默认为false,因此会将 a.so 库拷贝到手机上,随后 armeabi 下的 b.so 库也会被拷贝到手机上
- 如果首先扫描到 armeabi-v7a 包下的 a.so 库,此时会将 hasPrimary 置为true,并将 a.so 库拷贝到手机上,因为 hasPrimary 的值为 true ,后面也就不会拷贝 armeabi 包下的 b.so 库了
因此有可能只会拷贝 armeabi-v7a 下的 a.so 库到手机上,当执行 b.so 中的逻辑时,就会产生 crash
- android5.0+ 的 so 文件拷贝逻辑
不再以文件为粒度匹配,直接拷贝整个文件夹。因此只会将整个 armeabi-v7a 包下的 so 库拷贝,因此当执行 b.so 中的逻辑时,也会产生crash
正确的放置方法
- 为了减小 apk 体积,只保留 armeabi 和 armeabi-v7a 两个文件夹,并保证这两个文件夹中 so 数量一致
- 对只提供 armeabi 版本的第三方 so,原样复制一份到 armeabi-v7a 文件夹