Android 中与 so 有关的一个大坑
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 的。
- 为了减小 apk 体积,只保留 armeabi 和 armeabi-v7a 两个文件夹,并保证这两个文件夹中 so 数量一致
- 对只提供 armeabi 版本的第三方 so,原样复制一份到 armeabi-v7a 文件夹
Android 中与 so 有关的一个大坑的更多相关文章
- Android中调用C++函数的一个简单Demo
这里我不想多解释什么,对于什么JNI和NDK的相关内容大家自己去百度或谷歌.我对Android的学习也只是个新手.废话少说直接进入正题. 一.在Eclipse中创建一个Android Applicat ...
- Android中在activity中弹出一个popwindow
//-----在onCreate方法--中------创建popwindow布局 --pop_item-------------------------- View view=Lay ...
- android中返回数据给上一个活动,可以用来回显数据
(一)who简介:没错,就是startActivityForResult()方法,这个方法用来在活动被销毁的时候返回数据给上一个方法.参数说明: startActivityForResult(inte ...
- Android中一个经典理解误区的剖析
今天,在Q群中有网友(@广州-包晴天)发出了网上的一个相对经典的问题,问题具体见下图. 本来是无意写此文的,但群里多个网友热情不好推却,于是,撰此文予以分析. 从这个问题的陈述中,我们发现,提问者明显 ...
- Android中的sharedUserId属性详解
在Android里面每个app都有一个唯一的linux user ID,则这样权限就被设置成该应用程序的文件只对该用户可见,只对该应用程序自身可见,而我们可以使他们对其他的应用程序可见,这会使我们用到 ...
- Android中@id与@+id区别
Android中的组件需要用一个int类型的值来表示,这个值也就是组件标签中的id属性值. id属性只能接受资源类型的值,也就是必须以@开头的值,例如,@id/abc.@+id/xyz等. 如果在@后 ...
- Android中Activity启动模式详解
在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作.在Android中Activity的启动模式决定了Activity的启动运行方式. An ...
- Android中滑屏初探 ---- scrollTo 以及 scrollBy方法使用说明
今天给大家介绍下Android中滑屏功能的一个基本实现过程以及原理初探,最后给大家重点讲解View视图中scrollTo 与 scrollBy这两个函数的区别 . 首先 ,我们必须明白在Android ...
- 【转】整理一下Android中的ListView
原文网址:http://sunbofu.blog.51cto.com/6431507/1280441 Android中的listview目测是一个使用频率很高的组件,所以今天来总结一下listview ...
随机推荐
- java HTTP请求工具
package HttpRequestTest; import java.io.BufferedReader; import java.io.InputStream; import java.io.I ...
- postgresql 安装使用
www.postgresql.org去下载你需要的版本,我下载的9.6.8 安装过程中会让你输入一次密码,其余的默认就ok 默认超级用户名postgres 打开 pgadmin4,我们将语言改为中文会 ...
- SpringBoot学习之启动探究
SpringApplication是SpringBoot的启动程序,我们通过它的run方法可以快速启动一个SpringBoot应用.可是这里面到底发生了什么?它是处于什么样的机制简化我们程序启动的?接 ...
- TP中的AJAX返回ajaxReturn()
系统支持任何的AJAX类库,Action类提供了ajaxReturn方法用于AJAX调用后返回数据给客户端.并且支持JSON.XML和EVAL三种方式给客户端接受数据,通过配置DEFAULT_AJAX ...
- weblogic静默方式创建域
创建域目录:mkdir -p /home/weblogic/Oracle/Middleware/user_projects/domains/base_domain/ 创建文件: create_doma ...
- Django Views(视图函数)
http请求中产生两个核心对象: http请求:HttpRequest对象 http响应:HttpResponse对象 所在位置:django.http 之前我们用到的参数request就是HttpR ...
- SQL Server 扩展事件(Extented Events)从入门到进阶(4)——扩展事件引擎——基本概念
本文属于 SQL Server 扩展事件(Extented Events)从入门到进阶 系列 在第一二节中,我们创建了一些简单的.类似典型SQL Trace的扩展事件会话.在此过程中,介绍了很多扩展事 ...
- Cassandra User 问题汇总(1)------------repair
Cassandra Repair 问题 问1: 文档建议每周或者每月跑一次full repair.那么如果我是使用partition rangerepair,是否还有必要在cluster的每个节点上定 ...
- Scikit-learn:模型评估Model evaluation 之绘图
http://blog.csdn.net/pipisorry/article/details/53001866 绘制ROC曲线 def plotRUC(yt, ys, title=None): ''' ...
- PGM:不完备数据的参数估计
http://blog.csdn.net/pipisorry/article/details/52626889 使用不完备数据的贝叶斯学习:MLE估计(梯度上升和EM算法).贝叶斯估计. 表示:H[m ...