包体积优化

Android App包体积优化知识点记录


京东金融Android瘦身探索与实践文章知识点记录
https://juejin.cn/post/7214858677173518393

apk结构

  1. dex文件:应用程序内的 Java/Kotlin 源码最终会以字节码的方式存在于 classes.dex 文件中
  2. resources.arsc 存储values类型的资源,和其他大型资源的路径
  3. res/ 源码工程中 res 目录下除了 values 外的资源文件,这些文件路径同时会记录在 resources.arsc 中
  4. lib so文件
  5. assets/ 与 res/ 资源目录不同,assets/ 下的资源文件不会在 resources.arsc 中生成查询条目,且 assets/ 下的资源目录可完全自定义,在程序中通过 AssetManager 对象来获取。
  6. META-INF/该文件夹下主要包含 CERT.SF 和 CERT.RSA 签名文件, 以及 MANIFEST.MF 清单文件。
  7. AndroidManifest.xml 应用清单文件,用于描述应用基本信息,主要包括应用包名、应用id、应用组件、所需权限、设备兼容性等。

深入探索 Android 包体积优化上文章知识点记录
https://juejin.cn/post/6844904103131234311

  • dex 文件就将原来每个 class 文件中都有的共有信息合成了一体,这样做的目的是 保证其中的每个类都能够共享数据,这在一定程度上 降低了信息冗余,同时也使得 文件结构更加紧凑。与传统 jar 文件相比,Dex 文件的大小能够缩减 50% 左右

  • Proguard:在Android SDK里面集成的一个工具,它是一个免费的 Java 类文件 压缩、优化、混淆、预先校验 的工具,它的作用:瘦身+安全,优化包括 内联、修饰符、合并类和方法等 30 多种优化项,混淆之后生产mapping.txt文件就是 混淆规则

  1. minifyEnabled true //是否进行混淆
  2. shrinkResources true//移除无用的resource文件,资源压缩工具默认是采用安全压缩模式来运行,可以通过开启严格压缩模式来达到更好的瘦身效果。
  3. zipAlignEnabled true //开启zipAlign可以让安装包中的资源按4字节对齐,这样可以减少应用在运行时的内存消耗
  • 在 AndroidMainfest 中的类默认不会被混淆,所以四大组件和 Application 的子类和 Framework 层下所有的类默认不会进行混淆,并且自定义的 View 默认也不会被混淆。因此,我们不需要手动在 proguard-rules.pro 中去添加

  • D8 与 R8 
    D8Android Studio 3.1 或之后的版本 D8 将会被作为默认的 Dex 编译器,D8 的 优化效果 总的来说可以归结为如下 四点:

  1. Dex的编译时间更短
  2. dex文件更小
  3. D8 编译的 .dex 文件拥有更好的运行时性能
  4. 包含 Java 8 语言支持的处理

R8Proguard 压缩与优化部分的替代品,并且它仍然使用与 Proguard 一样的 keep 规则。如果我们仅仅想在 Android Studio 中使用 R8,当我们在 build.gradle 中打开混淆的时候,R8 就已经默认集成进 Android Gradle plugin 中了。

Android Studio 3.4 或 Android Gradle 插件 3.4.0 及其更高版本,R8 会作为默认编译器。否则,我们 必须要在 gradle.properties 中配置如下代码让 App 的混淆去支持 R8,如下所示:
android.enableR8=true
android.enableR8.libraries=true

  • R8与混淆
    R8:压缩比例大,kotlin友好
    混淆:处理枚举,算法强,能优化gson
    有了 R8,可以在一个步骤中完成脱糖、压缩、混淆、优化和 dex 处理 (D8),在之前混淆中,混淆和D8是分开的,产生两遍class文件,效率低

  • ASM统计使用的activity,可以确定无用的Activity(抖音的ByteX也可以统计)

  • Lint 来 扫描出重复的代码

  • 避免产生 Java access 方法(感觉没必要,收益较小,几百K)(直接使用抖音的ByteX)

深入探索 Android 包体积优化下文章知识点记录
https://juejin.cn/post/6872920643797680136

  • 点击项目右键,选中 Refactor,然后点击 Remove Unused Resource => preview 可以预览找到的无用资源,点击 Do Refactor 可以去除冗余资源(也属于Lint)

  • Android Lint 不会分析 assets 文件夹下的资源,因为 assets 文件可以通过文件名直接访问,不需要通过具体的引用,Lint 无法判断资源是否被用到

  • 通过 shrinkResources true 来 开启资源压缩,资源压缩工具只会把无用的资源替换成预定义的版本而不是移除,可以通过第一点来优化,也可以采用文章的自动删除方法,较复杂,推荐第一点

  • 图片压缩
    https://github.com/smallSohoSolo/McImage
    https://tinypng.com/
    需要注意的是,在 Android 的构建流程中,AAPT 会使用内置的压缩算法来优化 res/drawable/ 目录下的 PNG 图片,但这可能会导致本来已经优化过的图片体积变大,因此,可以通过在 build.gradle 中 设置 cruncherEnabled 来禁止 AAPT 来优化 PNG 图片,代码如下所示:
    aaptOptions {
    cruncherEnabled = false
    }

  • VD(纯色icon)->WebP(非纯色icon)->Png(更好效果) ->jpg(若无alpha通道)
    第一个是矢量图,它 仅仅需100字节的文件即可以生成屏幕大小的清晰图像,但是,Android 系统渲染每个 VectorDrawable 对象需要大量的时间,而较大的图像需要更长的时间。 因此,建议 只有在显示纯色小 icon 时才考虑使用矢量图形

  • AndroidResGuard
    https://github.com/shwenzhang/AndResGuard
    单独起一篇文章分析

  • resConfigs
    resConfigs “zh”, “zh-rCN”,通过 resConfig 来配置使用哪些语言,从而让构建工具移除指定语言之外的所有资源

  • So
    defaultConfig {ndk {abiFilters ‘arm64-v8a’}}

  • 插件化
    单独讲解

  • 包体积监控
    https://github.com/Tencent/matrix

得物篇
https://mp.weixin.qq.com/s/mVauS73sNcejzMR9nmrFPg

1.图片压缩
使用 cwebp 对图片进行webp转换,
使用 guetzli 对JPEG进行压缩,
使用pngquant对PNG 进行压缩,
使用 gifsicle 对gif进行压缩。
在实施对过程中,对于 res 目录下的文件优先使用 webp 处理,对assets 目录下的文件则进行同格式压缩

2.资源去重

3.资源混淆
用长路径替换短路径

4.ARSC压缩
Arsc 压缩降低的体积非常可观,但是 Target Sdk 在30以上 arsc 压缩被禁了。压缩 resources.arsc 虽然能带来包体上的收益,但也有弊端,它将带来内存和运行速度上的劣势。不压缩的resources.arsc系统可以使用mmap来节约内存的使用(一个app的资源至少被3个进程所持有:自己, launcher, system),而压缩的resources.arsc会存在于每个进程中。

5.动态加载so


工具类

dex文件优化,代码优化

  1. 字节码优化:
    去除冗余赋值,删除无副作用代码,短方法内联,R文件内联,https://github.com/bytedance/ByteX
  2. 资源优化:
    字节在微信的AndResGuard的基础上进一步优化
  3. redex(facebook开源)
    参考隔壁文章(包体积优化Redex分析)
  • facebook使用Dex 压缩 的方式,而且它 将 Dex 压缩后的文件都放在了 assets 目录中(很复杂)

  • XZ Utils 压缩dex,原理暂时不清楚

  • 7zip压缩,原理暂时不清楚

  • Lint 来 扫描出重复的代码

资源优化

  • AndResGuard是微信推出资源优化工具(目前不维护,gradle升级后有bug),它的基本思想类似于 ProGuard 中的混淆,通过解压APK后,将资源文件名进行短链处理比如res/layout/hello.xml转换为r/l/a.xml后,然后更改resources.arsc对应的value值,达到整体的瘦身效果。该项目优化目标为资源文件目录 res 内的文件,其优化点如下:
    1)对重复的资源文件,以计算 md5 值的方式来判断是否重复并只保留一份;
    2)对资源文件名称进行缩短,即名称混淆;
    3)对 APK 中的内容采取 7zip 压缩优化;

  • 7zip压缩,原理暂时不清楚

  • 配置CPU架构,根据不同的CPU架构,构建不同的类型的安装包,目前主流设备都是64位机器,因此安卓市场上主要投放的是依据arm64-v8a编译构建的安装包
    ndk {abiFilters arm64-v8a}

  • Android R+ 不在允许app压缩resource.asrc,对其进行压缩会影响启动速度和内存指标

  • 在 Android 6.0 上开启了 android:extractNativeLibs=”false” 的话,So 文件也不能被压缩

  • 接入的大量SDK中加入了几十种语言一样,导致整个体积变大,经过评估可以通过配置 resConfigs 去除无用的语言资源。
    defaultConfig {resConfigs “zh”,”en”}

  • shrinkResources:编译过程中用来检测并删除无用资源文件,也就是没有引用的资源,minifyEnabled:用来开启删除无用代码,比如没有引用到的代码,所以如果需要知道资源是否被引用就要配合minifyEnabled使用,只有两者都为true时才会起到真正的删除无效代码和无引用资源的目的。其作用是将未被引用的资源文件替换为一个体积很小的格式文件(仍存在占位体积,同时保留了该资源条目,所以 resources.arsc 体积并不会减少)

  • 尽量少用枚举类型,因为枚举在编译成字节码后,会增加大量体积(22行代码编译后字节码是86行)

  • drawable 和 mipmap 文件夹,右键后选择 convert to webp,将图片转为 WebP 格式(不一定会减少)

  • Lottie代替帧动画

  • 点击项目右键,选中 Refactor,然后点击 Remove Unused Resource => preview 可以预览找到的无用资源,点击 Do Refactor 可以去除冗余资源(也属于Lint)


参考文章:
https://juejin.cn/post/6844904103131234311
https://juejin.cn/post/7104228637594877965
https://juejin.cn/post/7214858677173518393
https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzI1MzYzMjE0MQ==&action=getalbum&album_id=2385951151339913221&scene=173&from_msgid=2247491039&from_itemidx=1&count=3&nolastread=1#wechat_redirect