关于如何把包瘦身到25M的思考(一)

前言

笔者之前也做过包体积优化相关的工作。但是也仅仅局限于冗余资源清除,图片压缩和资源网络存储等方面的工作。目前看到有商业APP已经瘦身到了25M,真是震惊我一万年。基于以前的一些工作,和对目前主流的一些优化方案进行了总结整理,方便以后如果leader忽然开始要求进行APP瘦身专项的话,我可以直接拿来用。

基础工作

基础工作就是对整个项目资源进行整理。这个过程其实更偏向于是在还历史债。在项目野蛮扩展时,不可避免地会留下很多坑,在业务稳定后,我们也不得不将这些坑一点一点地填回去。

冗余资源

(1)频繁迭代的业务变动带来的肯定是大片大片的代码重构。我们一直建议业务模块化开发,并不一定是module,但是必须是一个独立的package层级,这样的一个好处就是,如果业务不想要这块代码了,我们可以直接把这块业务代码给删了。不过业务资源很难随着业务逻辑的删除而被去除,这里包括无用的String,XML,Image等等。

我们可以通过Android Studio的Analyze查找Unused Resources来找到这些资源,并进行删除。 但是更好的一个操作,就是通过lint的方式,限制Unused Resources的存在。

kotlin 复制代码
<lint>
    <issue id="UnusedResources" severity="error">
        <ignore path="res/drawable/image.png" /> <!-- 如果有不想检查的资源,可以在此忽略,可根据实际情况添加或删除 -->
    </issue>
</lint>

如果当前项目存在UnusedResources,会直接编译报错,规范项目资源。

当然,我们也经常会对一个类删删改改,导致类里会有很多unused import,撇开强迫症感官因素,删除冗余的代码,也是包体积优化的重要课题。 同样的,我们可以在lint中对unused import进行限制。

kotlin 复制代码
<lint>
    <issue id="UnusedImport" severity="error" />
</lint>

当存在unused import时,编译就会报错。

目前业内也会选择通过插件的形式进行监控是否有冗余的资源存在。通用的方案是

  1. 解析res文件下文件,进行记录,作为物料全集
kotlin 复制代码
         resolve(context.getTransformContext().getArtifact(Artifact.RAW_RESOURCE_SETS));
  1. 解析XML文件和R文件,将资源id记录在队列中
  2. 取全集和使用的资源集合进行对比,输出冗余的资源数据,并进行提示警告。

(2)图片资源也是一个优化的点,我们通常会对图片进行压缩,当然更好的操作是将图片放到CDN上。考虑到首页有些动画资源需要即时展示,因此我们通常考虑可以使用一些更少的体积的资源,比如原生view绘制代替image,使用lottie代替WebP资源。

(3)我们还可以将so文件放在远端,通过soLoader进行动态下发,注意判断下载结果,如果so对系统架构有要求,也应当进行判断。

(4)脚本打包,隔离32位和64位的abi架构。虽然市场上已经很少有32位的手机了。

kotlin 复制代码
android {
    defaultConfig {
        ndk {
            abiFilters 'arm64-v8a'
        }
    }
    applicationVariants.all { variant ->
        variant.outputs.each { output ->
            output.outputFileName = "${variant.name}-64bit-v${variant.versionName}.apk"
        }
    }
}

资源处理

APK解压缩后,通常由几部分组成:

  • res主要是存放图片资源
  • lib主要是存放so库,各个cpu架构
  • dex源码是java源码编译后生成的java字节码文件,因方法数限制拆分了多个dex
  • assets主要存放不需要编译处理的文件
  • resources.arsc是编译后的二进制资源文件,包括图片、文本索引等
  • META-INF 签名信息
  • AndroidManifest.xml 描述配置文件

我们通常主要是对dex文件, resources.arsc文件进行优化。

DEX文件

业务开发时,很难想象代码量也会影响到包体积,但是考虑到商业项目常常上百万行代码,其实这也是无可厚非。我们业务开发在日常需求时,保持良好的代码习惯,及时下线废弃的业务代码,保证代码风格简练清楚,真的抵得上基础线的同学千幸万苦的优化。在AB测试完毕,及时下线对照组,包体积优化这块真的可以把地板都磕烂了。

dex的生命周期可以简单视为:kt文件->.class->.dex

目前在class文件阶段的操作是通过插件删除冗余的代码。dex阶段则是通过redex进行优化。笔者对redex这个项目了解的较少,并且对于dex层级的优化持谨慎态度。相比之下,class优化的方案就比较多了,并且开发者的灵活性也更强。

class文件的优化在于删除冗余代码,这些冗余代码包括Log.i输出以及init方法里的默认值赋值,识别非必要短方法,将其内联,删除常量字段以及R.class 常量内联等等。

相关代码实现可阅读byteX的插件实例。大佬们对技术细节钻研的极致精神在这里体现的淋漓尽致。

资源优化

资源优化主要是针对图片资源和字符资源的优化。

我们在前面可以将大部分的图片都上传到CDN,并对需要本地存放的图片资源进行压缩。在这个基础上,我们可以通过插件的形式,将超过一定阈值的图片资源检索出来,并进行提示。

(1) 资源去重

扫描指定的资源目录(默认src/main/res),对每个资源文件计算crc32 值,通过crc32 值比对识别内容完全相同的文件,并记录输出,进行提示。

(2)resources.arsc资源优化

业内有,在资源去重基础上,用短路径替换长路径,以减小文件名和 arsc 中常量池二进制文件大小的方案。通过插件的形式手动更改业务资源的层级和路径长短,对于XML的冗余代码也会有改动。此外,也可通过开源库AndResGuard进行优化。AndResGuard可以将原始长路径(如 res/drawable-xhdpi/ic_icon.png)替换为短路径(如 r/d/a.png),缩短文件名长度以减小体积。

总结

一般的商业项目都会进行冗余资源处理。相比之下,后续的Dex优化和resources.arsc资源优化,优先级就很低了。即使是黑科技拉满的话,在这两块也很难凿出2成以上的产出。所以如果想要达到25M的效果,必然是通过插件化技术,将所有业务插件化,放在了远端。在这一块,我在后续针对目前几个通用的热插拔框架,进行学习一下,后续放出。

参考资源

得物 Android 包体积资源优化实践

抖音 Android 包体积优化探索:资源二进制格式的极致精简

抖音Android包体积优化探索:从Class字节码入手精简DEX体积

ByteX

相关推荐
yzpyzp5 分钟前
Android 的16 KB内存页设备需要硬件支持吗,还是只需要手机升级到Android15系统就可以
android·智能手机
yzpyzp8 分钟前
目前市面上arm64-v8a、armeabi-v7a设备的市占率有多少?为什么x86架构的手机越来越少?
android·gradle·cpu·ndk
yzpyzp44 分钟前
Android studio自带的Android模拟器都是x86架构的吗,需要把arm架构的app翻译成x86指令?
android·android studio·cpu
洞见前行1 小时前
Android应用程序启动流程详解(含源码)
android·逆向
亿刀1 小时前
【学习VPN之路】路由表
android·docker
亿刀1 小时前
【学习VPN之路】NET技术
android·flutter
coderhuo1 小时前
Android USAP简介
android
yzpyzp2 小时前
ndk { setAbiFilters([‘armeabi-v7a‘, “arm64-v8a“]) }
android·gradle·ndk
awp2583 小时前
小程序安卓ApK转aab文件详情教程MacM4环境
android·小程序
ganshenml5 小时前
【Android Studio】安装Trae插件后Android Studio 启动崩溃问题处理
android·ide·android studio