包体积优化-资源优化
包体积资源优化
资源优化基础
资源优化的对象分为两类:
- 一是资源查询表
resources.arsc
,部分优化操作会涉及到res/
及 R 文件的修改,但本质都是从 resources.arsc 出发的; - 二是原始资源文件,包括
res/和 assets/
。
resources.arsc 优化
resources.arsc
resources.arsc
文件结构见:[[resources.arsc]]
资源配置优化
资源的混淆
apk 资源混淆:AndResGuard
AndResGuard 微信开源的资源混淆插件,将冗余的资源名称换成简短的名字如 abc,资源压缩的效果要比代码瘦身的效果要好的多。
与 AndResGuard 中的资源路径混淆效果相似,都是尽可能缩短资源文件的路径长度,从而减少 ResTable_entry
的 value 大小。我们将路径 Hash 值转换到碰撞最少的位数,作为最终的混淆结果,其优点在于混淆结果基本上是固定的,无需 applymapping。除此之外,我们还较为激进地去掉了大部分文件的后缀名。
aab 资源混淆:aabResGuard
arsc 压缩
resources.arsc 的压缩体积收益很高,但对其进行压缩会影响启动速度和内存指标。具体原因是:系统在加载 arsc 文件时,若 arsc 文件未压缩,可使用 mmap 进行内存映射;若 arsc 文件被压缩了,则需要将其解压缩后读取到 RAM 缓冲区,会增加内存使用,也会拖慢启动速度。在业界大都压缩 arsc 的情况下,百度 APP 出于综合考量一直未对 arsc 文件进行压缩。无独有偶,官方出于同样的考虑,从 Android11 开始强制要求 resources.arsc 不可压缩且保持 4 位对齐,否则会直接安装失败。
https://developer.android.com/about/versions/11/behavior-changes-11#app-packaging
可见:[[Android11适配(API30 AndroidR)#App packaging and installation]]
ZIP 优化
- 压缩
目前我们使用的压缩算法有 7z
和 zopfli
,后者压缩率和压缩耗时都有明显增加,稳定性还在验证中。采用新的压缩算法时需特别注意两点,一是不要压缩 resources.arsc;二是注意压缩、 对齐、签名操作的顺序
- 文件路径优化 从 ZIP 结构可知,APK 中有三处体积与文件路径长度相关:
META-INF/
、压缩源文件数据区的 local file header
、中心目录区的 file header
,资源文件路径优化效果同样会体现在这里。同理控制 assets/ 下的文件路径也可带来体积收益。
res 目录:资源优化
从 arsc 中的资源 id 包含了偏移量信息,系统通过偏移量在 arsc 中定位资源。所以下图中的空白区域必须保留一个 4 字节的占位,以满足偏移量查询方式。我们正在对此部分做优化,宗旨是通过优化不必要的 configuration,达到减少对齐占位的目的。
图片资源优化
- 只保留一套 UI 设计的图
- png 压缩,之前用 py 脚本,发版本前会扫描 app 内所有大于 5k 的图片给压缩下
- py 脚本工具
- TinyPngPlugin Gradle插件
- webp 替换 png(图片从 png 到 tinypng 到 webp)
- 用小的.9 图拉伸,替换大图
- 图片资源远程下发: 如果本地有大图,且使用要求为未压缩,或者压缩之后仍然很大,可以适当的选择动态下载该图。;利用 Fresco 预下载图片接口下载到本地
- 图片着色器:相同的图片只是颜色不同的话,完全可以只放一个图片,可以在 xml 中操作或者在内存里操作 Drawable,完成颜色替换。
1
2
3
4
5
6
7
8
9
10
<ImageView
android:id="@+id/iv_select_music"
android:layout_width="12dp"
android:layout_height="12dp"
android:src="@drawable/ic_select_course_subject_arr"
app:layout_constraintBottom_toBottomOf="@+id/tv_select_music_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/tv_select_music_text"
app:tint="@color/c_line_gray" />
1
2
3
4
5
6
7
val drawableRes = ResourcesCompat.getDrawable(
resources,
resId, null
) ?: return
val tintIcon = DrawableCompat.wrap(drawableRes)
DrawableCompat.setTint(tintIcon, hintColor)
setImageDrawable(tintIcon)
无用资源的移除
- lint 工具扫描无用的资源,这个优化有限,因为 R8/proguard 会移除无用资源,不会打包到 app/aab 中去
- 指定语言,删除无用语言资源:通过 gradle 的
resConfig "zh","ar"
,只保留必须的语言资源 - Gradle 配置 shrinkResources (需要配合 minifyEnabled=true) ;ShrinkResources 并不会真正移除未被引用的资源文件。不过我们可以拿到被 shrink 的 resources 列表,然后再利用资源优化工具做真正的删除
- 本地 so 库,二方三方库和分辨率
New ShrinkResource
2022.1 发布的 AGPv7.1.0 更新了资源缩减功能,添加了实验性选项 android.experimental.enableNewResourceShrinker.preciseShrinking
,该选项设置为 true 后 ShrinkResources
会完全移除未使用的文件资源及 value 资源,但 arsc 中仍会存在这些资源的填充占位。
重复资源优化
在大型 App 的开发过程中,可能涉及到多个团队和多个人员进行开发,每个人员在提交代码时可能会导致资源名称相同,导致了资源文件覆盖,然后根据模块名称添加前缀,解决覆盖的问题,可能导致了相同的资源文件导入了多分,从而增大了应用的体积。这时需要将公共资源提取出来并去重。
怎么识别重复资源?
- 编写脚本来遍历资源文件夹,检查文件的哈希值或内容,从而识别重复的资源。例如,可以使用 Python 脚本来实现这一点。
Layout 二进制文件裁剪优化
si 裁剪有有个 700K 优化空间
Json 文件压缩
如 Lottie 的 json 文件。
原理就是在编译过程中找到 merged_assets
这个文件夹:去掉空格和换行
- Debug 包差不多 assets 能减少 600 多 K
- 最终打包到 apk 也就 40 KB 左右
- https://github.com/hacket/GradlePluginDemos/tree/master/GradlePlugin8.0-Kotlin-New