Android apk瘦身最佳实践(二):代码混淆和资源压缩

要尽可能减小 APK 文件,我们应该启用压缩来移除发布构建中未使用的代码和资源。

1. 使用 ProGuard 混淆代码

在 Android 中代码混淆和压缩都是通过 ProGuard 来实现的,ProGuard 会检测和移除代码中未使用的类、字段、方法和属性,除此外还可以优化字节码,移除未使用的代码指令,以及用短名称混淆类、字段和方法。

在 build.gradle 中,使用 minifyEnabled 属性来开启代码混淆:

android {
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'),
                    'proguard-rules.pro'
        }
    }
    ...
}

2. 使用 shrinkResources 压缩资源

在 build.gradle 中使用 shrinkResources 属性来开启资源压缩,它在构建 apk 时可以移除那些没有引用到的资源文件,通常它必须与 minifyEnabled 属性一起使用:

release {
    shrinkResources true
    minifyEnabled true
    proguardFiles getDefaultProguardFile('proguard-android.txt'),
            'proguard-rules.pro'
}

需要注意的是,开启该属性设置后,它并不会移除 values/ 文件夹中定义的资源,例如:字符串、颜色、样式等等。

3. 混淆压缩对比

为了鉴定一下代码混淆与资源压缩到底有多大作用,我们随便找个简单的 app 工程来实验一下。

minifyEnabled shrinkResources apk大小
false false 4,285,579 字节,约4.3M
true false 3,781,347 字节,约3.8M
true true 3,779,525 字节,约3.8M
false true 无法编译

从中我们可以得到一些结论:

  1. 开启代码混淆和资源压缩后,apk 大小减少了约0.5M,一个小工程都尚且如此,在一个比较庞大的工程中开启这俩选项,压缩的大小还是很可观的。
  2. 开启资源压缩与不开启相比,只减少了约2000多字节的大小,可见大头还是靠代码混淆,资源压缩只能起到锦上添花的作用。
  3. 关闭代码混淆开启资源压缩,你会发现无法编译,Android Studio 会提示你这俩必须配对使用,要开启资源压缩必须得开启代码混淆。

4. 资源压缩会保留文件名不保留内容

我们再做个测试,在应用的资源中放一张png图片姑且命名为 test.png,放一个layout布局文件命名为test.xml,并且确保这2个资源文件没有任何代码会引用,我们开启代码混淆和资源压缩打包生成apk文件。

生成apk文件后,直接将apk包拖到 Android Studio 中,可以查看apk包的相关信息,细心一点的话你会发现几个有趣的现象:

  1. 在图片资源文件夹中,仍然可以看到名为 test.png 的图片,但是你打开看到的是一个 1*1 的图片;
  2. 在 layout 文件夹中,仍然可以看到名为 test.xml 的文件,打开看到的是一个空的xml文件,没有任何xml节点信息;

也就是说,资源压缩后,并不是直接删除了没有用到的资源文件,而是生成了对应的占位文件,这些占位文件都是空文件,相比原文件大小可以忽略不计。这样做的原因是:每个资源文件都对应 R.class 里的一个资源 id ,资源 id 的映射关系会打包在 resources.arsc 文件里,如果直接删除了资源文件,则可能需要同时修改 resources.arsc 里的资源映射表,这样是很繁琐的过程,而用占位文件来替换则避免了这种情况。

5. 使用 resConfigs 去除多余的语言包

目前很多第三方库内包含了各种语言包,但是大多数情况下我们的应用是不需要国际化,仅仅需要中文的就可以了,所以可以 resConfigs 配置来去掉多余的语言包:

android {
    defaultConfig {
        ...
        resConfigs "zh"
    }
}

如上所示,打包时只会将中文的资源包打进去,这样也可以一定程度上减小 apk 的体积。

6. ProGuard 中的压缩优化配置

关于 ProGuard 的配置有很多,具体需要查看文档才能知道作用是什么。刚开始接触的时候,我以为 ProGuard 只是做代码混淆而用的,其实它的作用不仅仅如此,有2个配置我们经常会忽略掉:

  • -dontshrink
    声明不进行压缩操作。
  • -dontoptimize
    不对 class 进行优化,默认是开启优化的。由于优化会进行类合并、内联等,使用热修复的应用,建议关闭优化。

我发现很多第三方库,例如友盟统计SDK,官方给出的 ProGuard 配置都需要加上该配置,也就是说不进行代码优化、压缩,往往初次接触者不明所以的照搬了。但其实如果你的应用没有采用热更新方案之类的,在 ProGuard 里去掉这2个配置,你会发现这对减小 apk 的大小效果很显著。

以我们自己的一个应用为例,ProGuard 里加上这2个配置打出来的 apk 包大小约为 25.5M,去掉这2个配置之后打出的包大小约为 24.1M。简直不敢相信,就这么2个小小的配置,居然能让包大小缩减 1.4M 左右,效果非常明显。

但是,实操过程中,我们发现有些页面的图标丢失变成黑色了,这些因为 ProGuard 判定某些资源文件没有被使用,将它转换成了一个 1*1 的占位文件了。这需要我们手动在 res/raw/keep.xml 里配置,明确告诉 ProGuard 哪些资源文件是不需要混淆压缩的,以为自己的一个配置为例:




7. 小结

采用 ProGuard 是减小 apk 大小非常有效的方法之一,但是使用过程中可能附带很多问题:有可能打包不成功、有可能图片资源问题丢失、有可能莫明其妙的闪退,这些都需要我们对 ProGuard 的配置有个基本的了解,遇到问题才能迎刃而解,用好了它相信会有很大的帮助。

系列文章
Android apk瘦身最佳实践(一):去除R.class
Android apk瘦身最佳实践(二):代码混淆和资源压缩
Android apk瘦身最佳实践(三):资源混淆原理
Android apk瘦身最佳实践(四):采用AndResGuard进行资源混淆
Android apk瘦身最佳实践(五):图片压缩
Android apk瘦身最佳实践(六):采用D8编译器

你可能感兴趣的:(Android apk瘦身最佳实践(二):代码混淆和资源压缩)