Android中so库的引用问题

转载 2016年08月28日 19:15:43
作者:Caspar
链接:https://zhuanlan.zhihu.com/p/21359984
来源:知乎

Android 应用开发中不可避免的会引入第三方的代码。如果是开源项目风险相对可控,如果引入商用的 SDK 那就要谨慎了,难免会有这样或那样的问题。比如我们今天要说的这一个。

对集成过第三方 SDK 的同学,上图中的目录结构应该不陌生。正常情况下我们只需要将不同版本的 .so 文件分别放置。但如果我们要集成的这个第三方 SDK 偏偏没有 arm-v7a 的版本呢?是删除 armeabi-v7a 目录只保留 armeabi ?还是说两个目录下 .so 文件数不同也没有关系?系统会加载哪个 .so 呢?

如果只对结论感兴趣可以直接跳到最后

为了方便说明我们先引入 FAT Binary 的概念。我们知道不同的 CPU 支持的指令集也不一样,那么如果我们需要让 App 尽可能不同的 CPU 上都可以正常运行该怎么做呢?简单,只需要将不同版本的 Binary 放在一个文件里,运行时按需取用就可以了。这就是 FAT Binary 的典型实现。Android 实现 FAT 的方式有些不同,就是上边提到的将 .so 文件放置在相应文件夹中。在 Android 系统中 ndk 默认会生成如下 7 种 .so。

在 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 我们是不需要的。

我们回到开头提到的问题:

假定我们现在的情况是这样的(b.so 就是那个只有 armeabi 版本的第三方 .so):

如果这样放置的话,在 ARM / ARM v7 两种设备上运行 apk 时会分别执行哪个 .so 呢?

答案是:不确定……

这么坑爹的答案是怎么来的呢?

由于 Android 上 FAT binrary 的设计如此阳春,在 apk 安装时就需要根据 CPU 情况执行对应版本 .so 的拷贝。对上边的情况最合理的一种做法应该是使用 armeabi-v7a/a.so 和 armeabi/b.so 这两个文件。Google 最初也是这么想的,然后就引入了 Bug…

Native library copy issue when install apk with different abi native libraries on device

上图是到 Android 4.4 还在使用的 .so 文件拷贝逻辑,看起来没有问题?

坑爹是 Android 在安装 apk 文件时没有保证 zip entry 的扫描顺序,所以同样的文件放置会带来两种不同的安装结果:



看的有点头晕?简而言之,如果按我们上面的放置方式,安装后系统可能只拷贝了 armeabi-v7a/a.so。如果执行到 b.so 的逻辑,程序显然会 crash。

这边还有个小插曲,这个 bug 的发现者在提交时其实已经给出了完善的解决方案,但在经历了快有小一年的 code review 后 Android 官方表示:我们自己另起炉灶修好了=_=。


这个问题确实在 Android 5.0 已经 “修复” 了。“修复” 方式简单粗暴,不再以文件为粒度匹配 abi,直接拷贝整个文件夹=_=。所以如果按我们之前的放置方法,在 Android 5.0+ 如果执行到 b.so 也是一定会 crash 的。

上面提到,只保留 armeabi 文件夹从性能角度是不明智的。正确的做法是将 armeabi/b.so 复制一份到 armeabi-v7a/b.so. 这是由于 ARM v7 是前向兼容 ARM v5 的。


撇开上面曲折离奇的故事,放置 .so 文件的正确姿势其实就两句话:

  • 为了减小 apk 体积,只保留 armeabi 和 armeabi-v7a 两个文件夹,并保证这两个文件夹中 .so 数量一致
  • 对只提供 armeabi 版本的第三方 .so,原样复制一份到 armeabi-v7a 文件夹

Android开发报错:记一次奇怪的Android Studio报错

今天做一个项目,编译时,Android Studio报错:Fragments should be static such that they can be re-instantiated by the...
  • gh8609123
  • gh8609123
  • 2016-07-03 04:01:40
  • 838

Fragment源码分析

最近时间比较多,主要分析了了两块内容,一块是Http请求,像httpUrlConnection,httpClient,还有一些第三方库像Volley,Xutils等等,发现这些好的第三方库其实整体框架...
  • zjutkz
  • zjutkz
  • 2015-11-01 16:37:57
  • 1119

Android中Fragment学习心得

1 Fragment 介绍 Fragment作为Android最基本,最重要的基础概念之一,在开发中经常会和他打交道。Fragment,简称碎片,是Android 3.0(API 11)提出的(An...
  • chaoshenzhaoxichao
  • chaoshenzhaoxichao
  • 2018-01-07 21:35:56
  • 166

关于android studio在创建Fragment过程中support不被识别的解决方案

这里只是针对我自己在创建Fragment过程中的问题进行解决。 首先,support库要通过SDKmanager下载 其次,查看gradle中的版本号是否正确,这方面的资料很多,可以参考。 最后,等待...
  • sunjiaxing001
  • sunjiaxing001
  • 2015-11-09 16:24:45
  • 763

Android studio中正确引入so文件的方法

相信很多朋友在使用Android studio开发中,遇到过如何引入第三方so文件的问题,然而第三方官方仅仅给出了ADT环境下的集成方式。 Android studio中默认使用的是gradle编译方...
  • wulianghuan
  • wulianghuan
  • 2015-03-23 18:23:42
  • 158545

与 so 有关的一个长年大坑

Android 应用开发中不可避免的会引入第三方的代码。如果是开源项目风险相对可控,如果引入商用的 SDK 那就要谨慎了,难免会有这样或那样的问题。比如我们今天要说的这一个。 对集成过第...
  • GePriniusce
  • GePriniusce
  • 2016-07-29 03:33:32
  • 539

只保留armeabi的.so文件用来减小apk包(不保证100%不崩溃)

由于所有的x86/x86_64/armeabi-v7a/arm64-v8a设备都支持armeabi架构的.so文件,因此似乎移除其他ABIs的.so文件是一个减少APK大小的好技巧,但事实上并不是:这...
  • bin622
  • bin622
  • 2017-05-02 15:43:12
  • 647

Fragments

(一)Fragments要点: a) Fragment表现为一个Activity用户界面的一部分; b) 你可以合并多个fragment在一个单独的activity中; c) 也可以在多个act...
  • pipisky2006
  • pipisky2006
  • 2013-01-19 03:45:32
  • 2633

Android打包时报Error:Error: This fragment inner class should be static....错误

Android Studio打包时报Error:Error: This fragment inner class should be static....错误
  • androidfszl
  • androidfszl
  • 2017-03-22 11:05:44
  • 1811

Android Studio Module 中的So 文件和 App So文件合并问题

其实在开发中我们经常会遇到一些c++语言或者其他第三方的so文件,有时候我们,胡乱的方在app下面的libs对应的指令集文件夹下,缺发现根本就用不了,这个时候我们就找不到原因,明明我声明的,为何报错说...
  • yang19910524
  • yang19910524
  • 2016-11-21 23:26:02
  • 1537
收藏助手
不良信息举报
您举报文章:Android中so库的引用问题
举报原因:
原因补充:

(最多只允许输入30个字)