目录

包体积优化的一些总结

前言

最近一直在搞包体积的优化,有点点心得

并且在优化的过程中发现了原有工程的各种问题,这里总结一下。

包体积优化的一些手段

Dex 优化

R8 和 ProGuard 配置优化

  1. R8 目前已经是默认的编译优化工具,只需要 minifyEnable = true
    1. 基础功能
      1. code shrink 删减
      2. obfuscation 混淆
      3. optimization 优化
    2. R8 fullMode 模式
      1. 包体积还可以继续缩减
      2. 遇到的问题 [R8 fullMode Constructor 问题]
        1. Gson 解析问题
        2. kotlin data class
      3. 解决办法
        1. -keepclasseswithmembers class * { (…); @com.google.gson.annotations.SerializedName ; }
  2. ProGuard 配置优化
    proGuard 描述了代码混淆和删减的规则,代码删减直接缩减 dex 体积,代码混淆会使类名、方法名、变量名更短从而缩减 dex 体积。
    目前 proGuard 配置存在的一些问题:
    1. 重复
      -keepclassmembers class .entities. { ; }
      -keepclassmembers class com.xingin.xhs.model.entities.
      * { *;}
      // 范围很大
      -keep @Kotlin.Metadata class * { *; }
    2. 删除 kotlin metadata 注解
      1. kotlin metadata keep 规则导致所有 kotlin 代码被 keep
      2. 引发了一些负负反而得正的潜在问题(Gson 解析)
    3. 删除 kotlin Intrinsics
    4. 优化 optimize 并没有开启
      1. -dontoptimize:删除这条 keep 规则,才会开启 R8 的代码优化
      2. optimize 的副作用:插件化打包的内联问题
  3. 隐性存在的反射大家没有在意
    需要 Gson 解析的类的成员变量被混淆了,到底对于解析有没有影响?
    1. Gson 的解析,默认依赖反射,除非有注解
    2. 反射效率和启动耗事问题

R.class 优化

R.class 内的值,可以直接内联

注意的问题:

  1. R$xxx.class 反射会失败
    1. ConstraintLayout id 问题 (ConstraintHelper)
      1. [Image: image.png]
  2. getIdentifier() 会被影响么?
    1. resources.getIdentifier(“status_bar_height”, “dimen”, “android”)
    2. 不会影响,依赖 resources.arsc

booster、bytex、AGP 4.1.0 + 移除 R keep 均可以实现 R.class 删除

DebugInfo 复用

DebugInfo 原理可以看之前的文章

插件化

业务代码可以作为插件单独下发

Resource 优化

AndResGuard 优化

微信的资源打包方案,可以对资源路径进行混淆,merge 重复资源并且压缩资源 AGP 4.2.0 以后,也可以实现资源混淆

  1. 资源混淆 更短的路径和资源名称
  2. 资源压缩 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(这其实可能会为后续的运行时性能的劣化,埋下伏笔)
  3. 影响 getIdentifier() 方法调用,有大量资源 keep,需要进一步整理

Resource 和 Resources.arsc 优化

资源数量的缩减,会影响到 资源本身的大小 和 resources.arsc 的大小,还有 META-INF/MANIFEST.MF 的大小(因为会计算每个资源 hash 值)

针对 resources.arsc 的单独优化有很多,但都需要修改 arsc 文件,目前可以简单采用以下方式优化:

  1. 更优的 resconfig,小包和极小包上有使用
    1. 可以配置分辨率和多语言支持情况 ,减少资源文件,减少 resources.arsc 大小
  2. shrinkResource
    1. 默认的 safeMode 的策略 —— 模糊匹配,不删除资源
    2. strictMode 更激进的策略
      1. keep.xml
      2. 影响 getIdentifier(),会有运行时 crash
    3. 最新的 AGP 中提供了新版本的 Shrinker,似乎在解决上述问题,并且进一步优化 resource.arsc 文件
  3. 图片 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 的动态下发功能。 但是,同时也需要考虑各种异常情况的兜底(下载失败、解压出错、依赖缺失等等)