文章首发于公众号「 技术最TOP 」
,天天都有干货文章持续更新,能够微信搜索「 技术最TOP 」
第一时间阅读!
前几天啊,在公众号发了一篇文章《优化ApK大小之ABI Filters 和 APK split》,评论区收到了一些留言说,文章讲得不够深刻,关于系统是如何选择不一样abi下的so库的?当前APP该如何适配?该去掉哪些该保留哪些?都存在一些疑问。android
所以,决定亲自更文一篇,系统地讲一下关于Android CPU架构方面的一些东西,以及结合大厂APP如微信、支付宝、淘宝等APP的适配状况,分析咱们开发APP中该如何适配。本文涉及如下几个问题:微信
本篇文章中,就一一为你解答这些疑问。架构
ABI是英文Application Binary Interface的缩写,及应用二进制接口。app
不一样Android设备,使用的CPU架构可能不一样,所以支持不一样的指令集。 CPU 与指令集的每种组合都有其本身的应用二进制界面(或 ABI),ABI很是精确地定义了应用程序的机器代码应如何在运行时与系统交互。您必须为要与您的应用程序一块儿使用的每种CPU架构指定一个ABI(Application Binary Interface)。ide
ABI 包含如下信息:函数
如何重整 C++ 名称。工具
Android目前支持如下7种ABIs:性能
mips, mips64, X86, X86–64, arm64-v8a, armeabi, armeabi-v7a
当咱们想要在项目中使用 native(C/C++) 类库,咱们必须对要支持的处理器架构提供对应编译包。每一个处理器架构须要咱们提供一个或多个包含native代码的.so文件。gradle
默认状况下,为了使APP有更好的兼容性,咱们使用Android Studio 或者命令打包时,会默认支持全部的架构,但相应的APK size 会疯狂的增大。对于用户来讲,目标设备只须要其中一个版本,但当用户下载APK时,会所有下载(对用户来讲至关的不友好)。优化
怎么办呢?abifilters
为咱们提供了解决方案,abifilters
为咱们提供了选择适配指定CPU架构的能力,只须要在app下的build.gradle
添加以下配置:
android { defaultConfig { ndk { abiFilters 'arm64-v8a', 'x86_64' } } }
你可能看了上面的这些文字,还不能理解abi的做用,那么咱们就用一个简单的例子来讲明一下。
首先,咱们建立一个最简单的Hello world
应用,只有一个Activity和一个启动图标。咱们看如下打出来的apk:
没有任何的原生库使用,大小为2.1MB
,如今咱们为它添加多ABI原生库支持,咱们在项目中集成Realm
,而后打包。
看到没,apk大小从2.1MB
猛增长到11.2MB
,多了一个原生so库的文件夹,大小为8.8MB
,咱们来看一下它的详细信息:
如上图所示,Realm
为5种CPU架构生成了.so
库,分别是mips
、x86
、x86_64
、arm64-v8a
、armeabi-v7a
。增长了8.8MB
包的大小。可是这不是咱们想要的,咱们只想要适配咱们指定的的CPU架构,所以,咱们须要在gralde.build中添加abifilters
配置来完成咱们想要的效果:
android { compileSdkVersion 28 // 编译sdk版本 defaultConfig { applicationId "com.example.zhouwei.helloworld" minSdkVersion 15 targetSdkVersion 28 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" // 适配指定CPU架构 ndk { abiFilters 'arm64-v8a', 'x86_64' } } }
效果以下:
能够看到,只生成了咱们指定CPU架构的so文件,包的大小也减小了5.3MB
。
这时候,你可能会有一个疑问,Android 共支持7种CPU架构,那么,咱们在实际项目中该适配哪些CPU架构能保证最好的兼容,同时又最大限度的减小APK的大小?
在回答这个问题以前,咱们不妨看一下这些顶级巨头公司,他们是是如何适配的。
首先,咱们下载一些大厂的APK,看一下他们的适配状况,这里我分析了微信、手机QQ、支付宝和淘宝这4个APP的适配状况:
能够看到,微信适配的是arm64-v8a
(微信应该是最近才适配到arm64-v8a
,之前是armeabi
),支付宝和手Q适配的是armwabi
,淘宝适配的是armwabi-v7a
。各个APP适配的平台不太同样,可是他们有一个共同点,那就是它们只指定了一个平台。
等等,上面这些APP只适配了一中CPU架构,好比只适配了armwabi-v7a
,那若是APP装在其余架构的手机上,如arm64-v8a
上,会蹦吗?
要弄清楚这个问题,咱们得先搞清楚,ABI是如何工做的。
官方文档解释以下:
Android 系统在运行时知道它支持哪些 ABI,由于版本特定的系统属性会指示:
此机制确保系统在安装时从软件包提取最佳机器代码。
为实现最佳性能,应直接针对主要 ABI 进行编译。例如,基于 ARMv5TE 的典型设备只会定义主 ABI:armeabi。相反,基于 ARMv7 的典型设备将主 ABI 定义为 armeabi-v7a,并将辅助 ABI 定义为 armeabi,由于它能够运行为每一个 ABI 生成的应用原生二进制文件。
64 位设备也支持其 32 位变体。以 arm64-v8a 设备为例,该设备也能够运行 armeabi 和 armeabi-v7a 代码。但请注意,若是应用以 arm64-v8a 为目标,而非依赖于运行 armeabi-v7a 版应用的设备,则应用在 64 位设备上的性能要好得多。
许多基于 x86 的设备也可运行 armeabi-v7a 和 armeabi NDK 二进制文件。对于这些设备,主 ABI 将是 x86,辅助 ABI 是 armeabi-v7a。
上面这一段是否是有点看蒙了,这里我来简单解释如下。总的来讲,就是一个Android设备能够支持多种ABI,设备主ABI和辅助ABI,以arm64-v8a
为主ABI的设备,辅助ABI为armeabi-v7a
和armeabi
,以armeabi-v7a
为主ABI的设备,辅助ABI为armeabi
。
另外,x86 架构的手机都会包含由 Intel 提供的称为 Houdini 的指令集动态转码工具,实现对 arm .so 的兼容,也就是说有适配armeabi平台的APP是能够跑在x86手机上的。
前面说了ABI的工做原理,一个Android设备支持主辅ABI,那么他们具体是如何工做的呢?咱们以arm64-v8a
架构的手机为例:
对于一个cpu是arm64-v8a架构的手机,它运行app时,进入jnilibs去读取库文件时,先看有没有arm64-v8a文件夹,若是没有该文件夹,去找armeabi-v7a文件夹,若是没有,再去找armeabi文件夹,若是连这个文件夹也没有,就抛出异常;
若是有arm64-v8a文件夹,那么就去找特定名称的.so文件,注意:若是没有找到想要的.so
文件,不会再往下(armeabi-v7a文件夹)找了,而是直接抛出异常。
Exception:Java.lang.UnsatisfiedLinkError: dlopen failed: library “/***.so” not found
特别须要注意的状况是在命中了文件夹,而未命中so文件这种状况:
arm64-v8a
文件夹,没有找到须要的so文件,就不会再往下(armeabi-v7a文件夹)找了,而是直接抛出异常。所以,咱们须要在咱们的app中配置 abiFilter 配置,来避免一些未知的错误。
defaultConfig { ndk { abiFilters "armeabi"// 指定ndk须要兼容的ABI(这样其余依赖包里x86,armeabi,arm-v8之类的so会被过滤掉) } }
arm64-v8a
: 目前主流版本armeabi-v7a
: 一些老旧的手机x86 / x86_64
: x86 架构的手机都会包含由 Intel 提供的称为 Houdini 的指令集动态转码工具,实现对 arm .so 的兼容,再考虑 x86 1% 如下的市场占有率,x86 相关的两个 .so 也是能够忽略的armeabi/mips / mips64
: NDK 之前支持 ARMv5 (armeabi) 以及 32 位和 64 位 MIPS,但 NDK r17 已再也不支持,极少用于手机能够忽。目前手机市场上,x86 / x86_64/armeabi/mips / mips6
的架构,基本能够不不考虑了,它们的占有量应不多不多了,arm64-v8a
做为最新一代架构,应该是目前的主流,armeabi-v7a
只存在少部分老旧手机。
我试着在Google上查找,具体的市场占有数据,但没找到,可是从国民级应用微信只适配arm64-v8a
就能够看出,arm64-v8a
是目前的主流,而且还有一点,Google Play 从2019年8月开始,就强制APP适配arm64-v8a
,以慢慢淘汰32位的armeabi-v7a
。
这里就能够回答前面的两个问题了。
Q1
: 只适配了armwabi-v7a
,那若是APP装在其余架构的手机上,如arm64-v8a
上,会蹦吗?
A:
不会,可是反过来会。
由于armwabi-v7a
和arm64-v8a
会向下兼容:
armeabi
的APP能够跑在armeabi
,x86
,x86_64
,armwabi-v7a
,arm64-v8
上armwabi-v7a
能够运行在armwabi-v7a
和arm64-v8a
arm64-v8a
能够运行在arm64-v8a
上那咱们该如何适配呢?给出以下几个方案:
方案一
:只适配armeabi
优势:
基本上适配了所有CPU架构(除了淘汰的mips和mips_64)缺点:
性能低,至关于在绝大多数手机上都是须要辅助ABI或动态转码来兼容方案二
:只适配 armwabi-v7a
同理方案一,只是又筛掉了一部分老旧设备,在性能和兼容两者中比较平衡
方案三:
只适配 arm64-v8
优势:
性能最佳缺点:
只能运行在arm64-v8
上,要放弃部分老旧设备用户这三种方案都是能够的,如今的大厂APP适配中,这三种都有,大部分是前2种方案。具体选哪种就看本身的考量了,以性能换兼容就arm64-v8
,以兼容换性能armeabi
,两者稍微平衡一点的就armwabi-v7a
。
目前来讲,大多数的大厂APP用的都是armeabi
或armwabi-v7a
,只有像微信这种牛逼的APP,为了追求性能和用户体验,放弃了少部分设备,这也说得通吧,毕竟微信也不在意苍蝇那点肉。
其实到上一小节,本文就该结束了,但总感受优势意犹未尽,除了适配全部所有CPU架构外,就特么不能性能和兼容同时兼得吗?其实Google早有考虑。也是能够实现的那就是 abi split,分包,实现也很简单,在gradle 中添加以下配置:
android { ... splits { // Configures multiple APKs based on ABI. abi { // Enables building multiple APKs per ABI. enable true // By default all ABIs are included, so use reset() and include to specify that we only // want APKs for x86 and x86_64. // Resets the list of ABIs that Gradle should create APKs for to none. reset() // Specifies a list of ABIs that Gradle should create APKs for. include "x86", "x86_64", "arm64-v8a", "armeabi", "armeabi-v7a" // Specifies that we do not want to also generate a universal APK that includes all ABIs. universalApk false } } }
而后,就能为每一个CPU架构单独打一个APK,该apk 中就只包含一个平台,以下:
这样,又能保证性能,又能不额外增长APK的大小,同时又又很完美的兼容,由于能够为全部架构都单独打一个包,一举多得。
同时,Google Play 支持上传多个APK:
这样,就能根据不一样的CPU架构,下载不一样的包啦!
可是,可是,可是,很遗憾,国内的应用商店目前还不支持!
参考文章
以上就是本文的所有内容,若有错误,欢迎评论区指出。原创不易,若是你喜欢本文,欢迎点赞、转发、收藏三连一下。