Edward_jie

for you, my Hall of Frame

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

故事背景
今天公司项目打完包,突然发现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是啥
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文件这种情况:

比如命中了arm64-v8a文件夹,没有找到需要的so文件,就不会再往下(armeabi-v7a文件夹)找了,而是直接抛出异常。
如果你的项目用到了第三方依赖,如果只保留一个ABI的时候,建议在Build中加入ndk.abiFilters
例如:第三方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

https://blog.csdn.net/yearningseeker/article/details/118554430

https://blog.csdn.net/weixin_42600398/article/details/118601134

posted on 2022-02-16 18:12  Edward_诺  阅读(1317)  评论(0编辑  收藏  举报