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 ...
随机推荐
- VMWare - Ubuntu 64 (16.04)之扩容介绍
背景 貌似是一个老生常谈的问题哈,由于自己之前也没有弄过,今天正好有时间稍微折腾了一下. 这里就选择最简单的方式来为大家呈现. VMWare 的设置 没有什么可以过多说的,完全是图形操作.这里直接上图 ...
- jQuery简单笔记
jQuery 是一个 JavaScript 库,简化了 JavaScript 的编程. 语法:$(selector).action() selector 是字符串,表示HTML元素. 对象 符号 例子 ...
- Spring MVC运行流程
一.配置阶段 ①web.xml ②DispatcherServlet //Spring MVC总入口 ③配置初始化参数 //classpath:application.xml,用于配置无数个 ...
- jquery easyui datagrid动态改变title的值
title:'<input type="text" id="txtTitle1" style="background:none;border:n ...
- vue移动端组件库vux使用小记
1.首先安装vux:npm install vux 2.安装vux-loader:npm install vux-loader 3.确认是否已安装less-loader:npm install ...
- 蚂蚁代理免费代理ip爬取(端口图片显示+token检查)
分析 蚂蚁代理的列表页大致是这样的: 端口字段使用了图片显示,并且在图片上还有各种干扰线,保存一个图片到本地用画图打开观察一下: 仔细观察蓝色的线其实是在黑色的数字下面的,其它的干扰线也是,所以这幅图 ...
- Go 语言递归函数
递归,就是在运行的过程中调用自己. 语法格式如下: func recursion() { recursion() /* 函数调用自身 */ } func main() { recursion() } ...
- MySQL NOW() 函数
定义和用法 NOW() 返回当前的日期和时间. 语法 NOW() 实例 下面是 SELECT 语句: SELECT NOW(),CURDATE(),CURTIME() 结果如下所示: NOW() CU ...
- 初始化openresty开发环境
参考链接 https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-14-04 初始化git ...
- WiFi文件上传框架SGWiFiUpload
背景 在iOS端由于文件系统的封闭性,文件的上传变得十分麻烦,一个比较好的解决方案是通过局域网WiFi来传输文件并存储到沙盒中. 简介 SGWiFiUpload是一个基于CocoaHTTPServer ...