关于如何把包瘦身到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

相关推荐
帅得不敢出门4 小时前
Linux服务器编译android报no space left on device导致失败的定位解决
android·linux·服务器
雨白5 小时前
协程间的通信管道 —— Kotlin Channel 详解
android·kotlin
TimeFine7 小时前
kotlin协程 容易被忽视的CompletableDeferred
android
czhc11400756638 小时前
Linux1023 mysql 修改密码等
android·mysql·adb
GOATLong9 小时前
MySQL内置函数
android·数据库·c++·vscode·mysql
onthewaying10 小时前
Android SurfaceTexture 深度解析
android·opengl
茄子凉心10 小时前
Android Bluetooth 蓝牙通信
android·蓝牙通信·bluetooth通信
00后程序员张11 小时前
iOS 26 App 运行状况全面解析 多工具协同监控与调试实战指南
android·ios·小程序·https·uni-app·iphone·webview
2501_9160074712 小时前
iOS 混淆实战,多工具组合完成 IPA 混淆、加固与发布治理(iOS混淆|IPA加固|无源码混淆|App 防反编译)
android·ios·小程序·https·uni-app·iphone·webview
2501_9159184112 小时前
怎么上架 App?iOS 应用上架完整流程详解与跨平台发布实战指南
android·ios·小程序·https·uni-app·iphone·webview