今天公司项目打完包,突然发现apk的size从原来的50多M激增到85M,作为敏感的我,肯定是要查清楚原因的。
如何排查呢,肯定是结果出发,我们解压了最新的apk,对比上个版本的apk,看一下具体增大的文件是哪部分?
可以清晰地看到,lib文件夹下增加了很多文件。
看了一下这个周期的开发需求,我发现是有一个同学在处理接入一个第三方库的时候,增加了ndk的abiFilter种类,具体代码为:
android {
......
defaultConfig {
......
ndk {
abiFilters 'armeabi', 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
}
}
那么在此之前我确实对这部分内容没有很透彻的研究,借着此机会好好研究了一下。
so库NDK编译出来的动态链接库。
一些重要的加密算法或者核心协议一般都用c写然后给java调用。这样可以避免反编译后查看到应用的源码。
放置so文件的正确方式:
• 为了减小 apk 体积,只保留 armeabi 和 armeabi-v7a 两个文件夹,并保证这两个文件夹中 .so 数量一致
• 对只提供 armeabi 版本的第三方 .so,原样复制一份到 armeabi-v7a 文件夹
原则:你应该尽可能的提供专为每个ABI优化过的.so文件,但要么全部支持,要么都不支持:你不应该混合着使用。你应该为每个ABI目录提供对应的.so文件。
ABI是英文Application Binary Interface的缩写,也就是应用二进制接口。
不同Android设备,使用的CPU架构可能不同,因此支持不同的指令集。 CPU 与指令集的每种组合都有其自己的应用二进制界面(或 ABI),ABI非常精确地定义了应用程序的机器代码应如何在运行时与系统交互。您必须为要与您的应用程序一起使用的每种CPU架构指定一个ABI(Application Binary Interface)。
ABI 包含以下信息:
可使用的 CPU 指令集(和扩展指令集)。运行时内存存储和加载的字节顺序。Android 始终是 little-endian。在应用和系统之间传递数据的规范(包括对齐限制),以及系统调用函数时如何使用堆栈和寄存器。可执行二进制文件(例如程序和共享库)的格式,以及它们支持的内容类型。Android 始终使用 ELF。如何重整 C++ 名称。总而言之,abi就是帮助适配不同的CPU架构的。
ABI现状及原理分析Android 目前共支持7种CPU架构:
mips, mips64, X86, X86–64, arm64-v8a, armeabi, armeabi-v7a
市场占有率上,x86 / x86_64/armeabi/mips / mips6 的架构,基本可以不不考虑了,它们的占有量应很少很少了,arm64-v8a作为最新一代架构,应该是目前的主流,armeabi-v7a只存在少部分老旧手机。
难道我要全部写上?if so,我们的apk size岂不是无比巨大。
根据参考资料:微信适配的是arm64-v8a(部分渠道下载的apk可能适配的是armeabi-v7a,不做过多纠结,有可能采用的是动态下发的方案,具体不详,以前是armeabi),支付宝和手Q适配的是armeabi,淘宝适配的是armeabi-v7a。各个APP适配的平台不太一样,但是他们有一个共同点,那就是它们只指定了一个平台。
看到这里,有的同学就开始问了,上面这些APP只适配了一中CPU架构,比如只适配了armeabi-v7a,那如果APP装在其他架构的手机上,如arm64-v8a上,会crash吗?
实际上,一个Android设备可以支持多种ABI,设备主ABI和辅助ABI,以arm64-v8a为主ABI的设备,辅助ABI为armeabi-v7a和armeabi;以armeabi-v7a为主ABI的设备,辅助ABI为armeabi。
另外,x86 架构的手机都会包含由 Intel 提供的称为 Houdini 的指令集动态转码工具,实现对 arm .so 的兼容,也就是说有适配armeabi平台的APP是可以跑在x86手机上的。
下面以arm64-v8a架构的手机为例:
特别需要注意的情况是在命中了文件夹,而未命中so文件这种情况:
例如:第三方aar文件,如果这个sdk对abi的支持比较全,可能会包含armeabi、armeabi-v7a、x86、arm64-v8a、x86_64五种abi,而你应用的其它so只支持armeabi、armeabi-v7a、x86三种,直接引用sdk的aar,会自动编译出支持5种abi的包。但是应用的其它so缺少对其它两种abi的支持,那么如果应用运行于arm64-v8a、x86_64为首选abi的设备上时,就会crash了哦。
因此,我们需要在我们的app中配置 abiFilter 配置,来避免一些未知的错误。
defaultConfig {
ndk {
abiFilters "armeabi"// 指定ndk需要兼容的ABI(这样其他依赖包里x86,armeabi,arm-v8之类的so会被过滤掉)
}
}
我们该如何适配?
明确一个基本原则,abi是向下兼容的,即:
只适配armeabi的APP可以跑在armeabi,x86,x86_64,armeabi-v7a,arm64-v8上只适配armeabi-v7a可以运行在armeabi-v7a和arm64-v8a只适配arm64-v8a 可以运行在arm64-v8a上因此我们有如下适配方案:
只适配armeabi:
优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差
只适配 armeabi-v7a
只是又筛掉了一部分老旧设备,在性能和兼容二者中比较平衡
只适配 arm64-v8
优点:性能最佳(微信大哥采用的)
缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户
上述方案只是我们给出的建议,具体还要看实际的需求和考量:以性能换兼容就arm64-v8,以兼容换性能armeabi,二者稍微平衡一点的就armeabi-v7a。
能否做到性能+兼容都要上述方案多多少少有一些取舍的,有没有完美的方案,既不放弃性能,也能完全兼容。答案是肯定的,Google已经安排上了,通过abi split,分包,为每一个CUP架构打一个apk,该apk 中就只包含一个平台。 google play支持上传多个apk,但是,据我所知,目前国内的应用商店是不支持的。
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
}
}
}
本文大量参考如下文章,如有侵权,请联系本人删除。
参考文章:https://juejin.cn/post/6844904148589084680
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)