

## 1 资源优化

### 1.1 冗余资源优化

* 使用 Lint 的去除冗余资源
* 优化 shrinkResources 流程（使用 [android-arscblamer](https://github.com/google/android-arscblamer)）

### 1.2 重复资源优化

在团队开发中，一个项目，常常存在**资源名称不同，但是资源内容相同的情况**，可以在 Android 构建工具执行 package${flavorName}Task 之前通过修改 Compiled Resources 来实现去重。

* 通过资源包中的每个ZipEntry的CRC-32 checksum来筛选出重复的资源。

* 通过[android-chunk-utils](https://github.com/madisp/android-chunk-utils)修改resources.arsc，把这些重复的资源都重定向到同一个文件上。

* 删除重复资源。

### 1.3 图片压缩

使用网站 [tinypng](https://tingpng.com/) 进行图片压缩，也可以批量压缩 [McImage](https://github.com/smallSohoSolo/McImage/blob/master/README-CN.md)、[TinyPngPlugin](https://github.com/Deemonser/TinyPngPlugin) 或 [TinyPIC_Gradle_Plugin](https://github.com/meili/TinyPIC_Gradle_Plugin/blob/master/README.zh-cn.md) 的，AAPT 会使用内置的压缩算法来优化 res/drawable/ 目录下的 PNG 图片，使用如下代码禁止：

```java
aaptOptions {
    cruncherEnabled = false
}
```

图片格式选择:

VectorDrawable（纯色 icon）->WebP（非纯色icon）->png（更好效果） ->jpg（若无 alpha 通道）

### 1.4 资源混淆

将 **资源路径混淆成单个资源的路径**，使用 [AndroidResGuard](https://github.com/shwenzhang/AndResGuard/blob/master/README.zh-cn.md)，将冗余的资源路径变短，例如将 **res/drawable/wechat** 变为 **r/d/a**。

### 1.5 R Field 的内联优化

通过 Javassist 或者 ASM 字节码。

工具推荐：蘑菇街的 [ThinRPlugin](https://github.com/meili/ThinRPlugin/blob/master/README.zh-cn.md)

### 1.6 资源文件最少化配置

通过 resConfig 来配置语言，从而让构建工具移除指定语言之外的所有资源，通常国内只需要 中文即可，图片资源文件类同理。

```java
defaultConfig {
        resConfigs "zh", "zh-rCN"
        resConfigs "nodpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"
    }
```

### 1.7 其他

* 尽量只切一套图：hdpi
* 资源在线化：一些图片、音频资源可以放到网络上
* 统一应用风格：同意设计风格，分割线，占位图等

## 2 代码优化

### 2.1 Proguard

使用代码混淆，能够缩短变量和函数名以及丢失部分无用信息等方式，使应用包体积减小。

配置

```java
buildTypes {
    release {
        // 1、是否进行混淆
        minifyEnabled true
        // 2、开启zipAlign可以让安装包中的资源按4字节对齐，这样可以减少应用在运行时的内存消耗
        zipAlignEnabled true
        // 3、移除无用的resource文件：当ProGuard 把部分无用代码移除的时候，
        // 这些代码所引用的资源也会被标记为无用资源，然后
        // 系统通过资源压缩功能将它们移除。
        // 需要注意的是目前资源压缩器目前不会移除values/文件夹中
        // 定义的资源（例如字符串、尺寸、样式和颜色）
        // 开启后，Android构建工具会通过ResourceUsageAnalyzer来检查
        // 哪些资源是无用的，当检查到无用的资源时会把该资源替换
        // 成预定义的版本。主要是针对.png、.9.png、.xml提供了
        // TINY_PNG、TINY_9PNG、TINY_XML这3个byte数组的预定义版本。
        // 资源压缩工具默认是采用安全压缩模式来运行，可以通过开启严格压缩模式来达到更好的瘦身效果。
        shrinkResources true
        // 4、混淆文件的位置，其中 proguard-android.txt 为sdk默认的混淆配置，
        // 它的位置位于android-sdk/tools/proguard/proguard-android.txt，
        // 此外，proguard-android-optimize.txt 也为sdk默认的混淆配置，
        // 但是它默认打开了优化开关。并且，我们可在配置混淆文件将android.util.Log置为无效代码，
        // 以去除apk中打印日志的代码。而 proguard-rules.pro 是该模块下的混淆配置。
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        signingConfig signingConfigs.release
    }
}
```

### 2.2 D8 与 R8 优化

D8 优化：

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

```java
//AS3.0 需要开启 AS3.1 之后默认
android.enableD8 = true
```

R8 优化：

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

如果我们当前使用的是 Android Studio 3.4 或 Android Gradle 插件 3.4.0 及其更高版本，R8 会作为默认编译器。否则，需要在 gradle.properties 中配置：

```
android.enableR8=true
android.enableR8.libraries=true
```

### 2.3 使用 ReDex 进行分包优化、去除 debug 信息及行号信息

使用 [Redex](https://fbredex.com/docs/installation) 来 APk 进行优化。 



## 3 so 库优化

### 3.1  so 移除

```java
defaultConfig {
    ndk {
        abiFilters "armeabi" //armeabi 目录下的 So 可以兼容别的平台上的 So,只需要使用这个
    }
}
```

缺点：别的平台使用时性能上就会有所损耗，失去了对特定平台的优化。

### 3.2  so 移除优化

对于性能敏感的模块，它使用到的 So，们都放在 armeabi 目录当中随着 Apk 发出去，然后在代码中来判断一下当前设备所属的 CPU 类型，根据不同设备 CPU 类型来加载对应架构的 So 文件。

### 3.3  XZ Utils 对 Native Library 进行压缩

*  XZ Utils 进行压缩
* 解压： acebook 的 so 加载库 [SoLoader](https://github.com/facebook/SoLoader)，能够解压应用的 Native Library 并能递归地加载在 Android 平台上不支持的依赖项、

### 3.4 合并 Native Library

按照自身 App 的 so 情况来配置需要合并哪些对象。由于合并共享对象（即 .so 文件）在原先的构建流程中是无法实现的，因此 FaceBook 更改了链接库的方式，并把它集成到了构建系统 [Buck](https://github.com/facebook/buck) 中。该功能允许每个应用程序指定应合并的 .so 库，从而避免意外引入不必要的依赖关系。然后，Buck 负责为每个合并的 .so 库收集所有对象（文件），并将它们与适当的依赖项链接在一起。

### 3.5 删除 Native Library 中无用的导出 symbol

分析代码中的 JNI 方法以及不同 Library 库的方法调用，然后找出无用的 symbol 并删除，这样 Linker 在编译的时候也会把 symbol 对应的无用代码给删除，在 **Buck 有 [NativeRelinker](https://github.com/facebook/buck/blob/master/src/com/facebook/buck/android/relinker/NativeRelinker.java)** 这个类，它就实现了这个功能，其 **类似于 Native Library 的 ProGuard Shrinking 功能**。

### 3.6 动态下载 so 库

服务器动态下载 so 库，再使用。

## 4 推荐学习

Android 开发高手课

[Android App包瘦身优化实践](https://tech.meituan.com/2017/04/07/android-shrink-overall-solution.html)

[深入探索 Android 包体积优化](https://juejin.cn/post/6872920643797680136)

