包体积优化的一些总结
目录
前言
最近一直在搞包体积的优化,有点点心得
并且在优化的过程中发现了原有工程的各种问题,这里总结一下。
包体积优化的一些手段
Dex 优化
R8 和 ProGuard 配置优化
- R8 目前已经是默认的编译优化工具,只需要 minifyEnable = true
- 基础功能
- code shrink 删减
- obfuscation 混淆
- optimization 优化
- R8 fullMode 模式
- 包体积还可以继续缩减
- 遇到的问题 [R8 fullMode Constructor 问题]
- Gson 解析问题
- kotlin data class
- 解决办法
- -keepclasseswithmembers class * { (…); @com.google.gson.annotations.SerializedName ; }
- 基础功能
- ProGuard 配置优化
proGuard 描述了代码混淆和删减的规则,代码删减直接缩减 dex 体积,代码混淆会使类名、方法名、变量名更短从而缩减 dex 体积。
目前 proGuard 配置存在的一些问题:- 重复
-keepclassmembers class .entities. { ; }
-keepclassmembers class com.xingin.xhs.model.entities.* { *;}
// 范围很大
-keep @Kotlin.Metadata class * { *; } - 删除 kotlin metadata 注解
- kotlin metadata keep 规则导致所有 kotlin 代码被 keep
- 引发了一些负负反而得正的潜在问题(Gson 解析)
- 删除 kotlin Intrinsics
- 优化 optimize 并没有开启
- -dontoptimize:删除这条 keep 规则,才会开启 R8 的代码优化
- optimize 的副作用:插件化打包的内联问题
- 重复
- 隐性存在的反射大家没有在意
需要 Gson 解析的类的成员变量被混淆了,到底对于解析有没有影响?- Gson 的解析,默认依赖反射,除非有注解
- 反射效率和启动耗事问题
R.class 优化
R.class 内的值,可以直接内联
注意的问题:
- R$xxx.class 反射会失败
- ConstraintLayout id 问题 (ConstraintHelper)
- [Image: image.png]
- ConstraintLayout id 问题 (ConstraintHelper)
- getIdentifier() 会被影响么?
- resources.getIdentifier(“status_bar_height”, “dimen”, “android”)
- 不会影响,依赖 resources.arsc
booster、bytex、AGP 4.1.0 + 移除 R keep 均可以实现 R.class 删除
DebugInfo 复用
DebugInfo 原理可以看之前的文章
插件化
业务代码可以作为插件单独下发
Resource 优化
AndResGuard 优化
微信的资源打包方案,可以对资源路径进行混淆,merge 重复资源并且压缩资源 AGP 4.2.0 以后,也可以实现资源混淆
- 资源混淆 更短的路径和资源名称
- 资源压缩
apk 其实是标准的 zip 格式。
zipEntry 分为 Stored(不压缩) 和 Deflate
安卓资源打包时,默认会保持很多种类的资源不压缩,如 arsc 文件、png 文件等等
https://cs.android.com/android/platform/superproject/+/master:frameworks/base/tools/aapt2/cmd/Link.cpp
https://cs.android.com/android/platform/superproject/+/master:frameworks/base/tools/aapt/Package.cpp
而 resguard 重新打包压缩时,修改了这个规则,可以通过配置实现对应资源的压缩,可以使用 zip 命令查看 entry 的状态,会发现 resguard 处理之后大部分的 entry 变为 Deflate(这其实可能会为后续的运行时性能的劣化,埋下伏笔) - 影响 getIdentifier() 方法调用,有大量资源 keep,需要进一步整理
Resource 和 Resources.arsc 优化
资源数量的缩减,会影响到 资源本身的大小 和 resources.arsc 的大小,还有 META-INF/MANIFEST.MF 的大小(因为会计算每个资源 hash 值)
针对 resources.arsc 的单独优化有很多,但都需要修改 arsc 文件,目前可以简单采用以下方式优化:
- 更优的 resconfig,小包和极小包上有使用
- 可以配置分辨率和多语言支持情况 ,减少资源文件,减少 resources.arsc 大小
- shrinkResource
- 默认的 safeMode 的策略 —— 模糊匹配,不删除资源
- strictMode 更激进的策略
- keep.xml
- 影响 getIdentifier(),会有运行时 crash
- 最新的 AGP 中提供了新版本的 Shrinker,似乎在解决上述问题,并且进一步优化 resource.arsc 文件
- 图片 png → webp 和 各类资源压缩
Native So 优化
优先 c++_shared 和 更优的编译参数
如果编译时选择 c++_static,那么 so 中会包含一些 c++_stl 代码,多个 so 其实就存在了多份相同代码
可以选择 c++_shared,共享一份代码
编译、链接参数优化
https://www.algolia.com/blog/engineering/android-ndk-how-to-reduce-libs-size/
需不需要 strip?
AGP 中存在 strip task,所以 aar 中可以不 strip,视情况而定
so 合并压缩(不推荐)
将多个 so 合并压缩为一个文件
收益十分可观,但是这样一定程度上影响启动速度
插件化 和 so 动态下发
插件化功能,天然包含 so 的动态下发功能。 但是,同时也需要考虑各种异常情况的兜底(下载失败、解压出错、依赖缺失等等)