如何对包大小精简

1,622 阅读6分钟

在配置了crunchPngs(图像压缩) true的情况下,png图片的压缩是否会减少包大小

操作 包大小
crunchPngs false 71.6 MB (75,105,580 字节)
crunchPngs true 67.5 MB (70,803,404 字节)
tinypng+crunchPngs true 66.6 MB (69,890,184 字节)

-压缩图片信息

文件名 压缩前 crunchPngs压缩后 tinypng+crunchPngs压缩后 转webp无损压缩(未经crunchPngs)
bg_vip_home.png 793 KB (812,431 字节) 587.25K 76.45K 430 KB (441,232 字节)
gonglve_iv_1.png 799 KB (819,116 字节) 536.25K 133.83K 406 KB (416,394 字节)

推荐一个网站png图片压缩

由上面结论可以得出,压缩png图片在配置了crunchPngs.能大大减少图片资源的体积从而减少包的大小.
同样的通过自己精简png图片的方式也可以减少包大小.

crunchPngs配置的相关信息

1. 我们在build文件中配置了此项信息后gradle会把此配置读取到dsl文件中.并在执行打包action的时候处理此项配置.
2. 通过aapt进行资源的压缩.

aapt相关介绍文档,有兴趣的可以查看研究

aapt命令详解

aapt2工具介绍

分析apk包

我们可以通过build->output->apk->flavor名称(这里是neibu)->双击apk包来用studio打开apk包进行分析.

混淆配置文件的产物

  • dump.txt:描述apk文件中所有类的内部结构
  • mapping.txt:混淆前后的类、类成员、方法的对照关系(重要,追溯Crash堆栈信息要用到)
  • resources.txt:资源文件的压缩信息
  • seeds.txt:未被混淆的类和成员
  • usage.txt:被移除的代码

理解混淆的产物

移除无用代码的方式猜想.

  1. 因为通过seeds.txt可以看出未被混淆的类和成员,我们可以查看此文件,确定一下此文件中所记录的类和成员是否是必须不能被混淆,能被混淆的话我们可以通过修改混淆配置文件去混淆这些文件.
  2. 因为通过usage.txt可以看出被移除的代码,我们可以通过追踪此文件中自己手写的代码部分,然后手动移除无用代码.

代码压缩

  • 从应用及其库依赖项中检测并安全地移除未使用的类、字段、方法和属性

资源压缩

  • 移除未使用资源

从封装应用中移除未使用的资源,包括应用的库依赖项中未使用的资源。此功能可与代码压缩结合使用,这样一来,移除未使用的代码后,也可以安全地移除不再引用的任何资源

    要启用资源压缩,请在 build.gradle 文件中将 shrinkResources 属性设为 true
  • 严格引用审查

您的代码调用了 Resources.getIdentifier()(或您的任何库进行了这一调用 - AppCompat 库会执行该调用),这就表示您的代码是根据动态生成的字符串查询资源名称。当您执行这一调用时,资源压缩器默认情况下会采取防御性行为,将所有具有匹配名称格式的资源标记为可能已使用,无法移除。

以下代码会使系统将所有带 img_ 前缀的资源标记为已使用。

val name = String.format("img_%1d", angle + 1)
val res = resources.getIdentifier(name, "drawable", packageName)
  • 移除未使用的备用资源

Gradle 资源压缩器只会移除未由您的应用代码引用的资源,这意味着,它不会移除用于不同设备配置的备用资源。如有必要,您可以使用 Android Gradle 插件的 resConfigs 属性来移除您的应用不需要的备用资源文件。

例如,如果您使用的是包含语言资源的库(如 AppCompat 或 Google Play 服务),则 APK 将包含这些库中消息的所有已翻译语言字符串,无论应用的其余部分是否翻译为同一语言。如果您希望只保留应用正式支持的语言,可以使用 resConfig 属性指定这些语言。系统会移除未指定语言的所有资源。

以下代码段展示了如何将语言资源限定为仅支持英语和法语:

android {
        defaultConfig {
            ...
            resConfigs "en", "fr"
        }
    }

同样,您也可以通过编译多个 APK,让每个 APK 以不同的设备配置为目标,自定义要包含在 APK 中的屏幕密度或 ABI 资源。

  • 合并重复资源

默认情况下,Gradle 还会合并同名的资源,如可能位于不同资源文件夹中的同名可绘制对象。这一行为不受 shrinkResources 属性控制,也无法停用,因为当多个资源与代码查询的名称匹配时,有必要利用这一行为来避免错误。

只有在两个或更多个文件具有完全相同的资源名称、类型和限定符时,才会进行资源合并。Gradle 会在重复项中选择它认为最合适的文件(根据下述优先顺序),并且只将这一个资源传递给 AAPT,以便在 APK 文件中分发。

Gradle 会在以下位置查找重复资源:

与主源集关联的主资源,通常位于 src/main/res/。 变体叠加,来自编译类型和编译特性。 库项目依赖项。 Gradle 会按以下级联优先顺序合并重复资源:

依赖项 → 主资源 → 编译特性 → 编译类型

例如,如果某个重复资源同时出现在主资源和编译特性中,Gradle 会选择编译特性中的资源。

如果完全相同的资源出现在同一源集中,Gradle 无法合并它们,并且会发出资源合并错误。如果您在 build.gradle 文件的 sourceSet 属性中定义了多个源集,则可能会发生这种情况。例如,如果 src/main/res/ 和 src/main/res2/ 包含完全相同的资源,就可能会发生这种情况。

混淆

  • 缩短类和成员的名称,从而减小 DEX 文件大小。

优化

  • 检查并重写代码,以进一步减小应用 DEX 文件的大小。

      例1:如果 R8 检测到从未采用过给定 if/else 语句的 else {} 分支,R8 便会移除 else {} 分支的代码。
      例2:如果您的代码只在一个位置调用某个方法,R8 可能会移除该方法而将其内嵌在这一个调用点。
      例3:如果 R8 确定某个类只有一个唯一的子类且该类本身未实例化(例如,一个抽象基类仅由一个具体实现类使用),那么 R8 可以将这两个类组合在一起并从应用中移除一个类。
    

名词解释:

  1. minifyEnabled=true 会启用R8代码压缩

    是指移除 R8 确定在运行时不需要的代码的过程。例如,如果您的应用包含许多库依赖项,但仅利用它们的一小部分功能,那么此过程可以大大减小应用的大小。R8会自动为您生成大多数应用项目(如应用的 Activity、view和service)所需的保留规则以及ProGuard -keep作为不应舍弃的类和应用入口点.

  2. shrinkResources=true 压缩资源

    使资源压缩器确定应用仍然使用的资源,并移除未被使用的资源,此功能最好在设置了代码压缩的同时使用.这样就能够删除无用代码引用无用资源.

参考文献: