本文主要就 d8、Proguard 、R8 等工具实现对APK代码的缩减,优化和混淆做相关记录。
一、概念梳理
d8:是一种命令行工具,Android Studio 和 Android Gradle 插件使用该工具来将项目的 Java 字节码编译为在 Android 设备上运行的 DEX 字节码,该工具支持您在应用的代码中使用 Java 8 语言功能。
d8
还作为独立工具纳入了 Android 构建工具 28.0.1 及更高版本中:android_sdk/build-tools/version/d8
Proguard:优化Java 编译器生成的 Java 字节码(.class)代码,从而生成更小,更快的 Java 字节码,ProGuard 通常将字节码大小减少20-50%,并将字节码性能提高多达20%
R8:当您使用 Android Studio 3.4 或 Android Gradle 插件 3.4.0 及更高版本时,R8 是默认编译器,用于将项目的 Java 字节码转换为在 Android 平台上运行的 DEX 格式。
再放几个文档,帮助大家更好的理解:
二、APK的打包流程
细节可阅读本篇博客,这里只摘录部分: Android 加载流程(打包与启动)
DVM虚拟机Java代码从编写到执行的过程:
1) 编写Java代码
2) 所有的Java代码通过Java编译器(javac)编译成java字节码,即.class文件
3) Java字节码通过Android的dx工具转换成Dalvik字节码,即.dex文件
4) Dalvik字节码在Dalvik虚拟机上运行
下面聊聊这个过程的进化历史:
我们使用 Java 或者是 Kotlin 来编写程序代码,然后转换为 Android 设备所需要的 Dalvik 字节码(.dex),整个过程可以简单的用下图来表示:
分为以下几步来看:
1)传统的 Java 编译器将源代码编译为 Java 字节码(.class)
2)ProGuard 可以选择优化此代码,从而生成更小,更快的 Java 字节码。 ProGuard 通常将字节码大小减少20-50%,并将字节码性能提高多达20%
3)dx 编译器最终可以将此 Java 字节码转换为 Dalvik 字节码(.dex)
4) Dalvik 字节码打包在 apk 文件中,最终安装在设备上。根据 Android 的版本,字节码被及时(Dalvik 虚拟机),提前(ART 虚拟机)或两者的组合(Android P)解释、编译。
加:ART与DVM虚拟机的区别?
1.DVM虚拟机中应用每次运行时,字节码都需要通过即时编译器转换成机器码,这会使应用的运行效率降低,而在ART中,系统在安装应用时会进行一次预编译,将字节码预先编译成机器码并存储在本地,这样应用每次运行时就不需要执行编译,提升了运行效率。
2.ART由于将字节码预先编译成机器码存储在本地,所以ART虚拟机占用空间比Dalvik大。
2015年,谷歌 Android 团队引入了编译器 Jack 和 Jill 。他们只需一步即可集成 Java 编译器、ProGuard 和 Dalvik 编译器的功能:
它极大地简化了构建过程,但对于使用 Java 字节码的语言和工具的生态系统并没有起到很好的作用。 Android 团队在2017年放弃了它。使用新的 D8 编译器,他们退后一步,只需用新的实现替换 dx 编译器:
整个过程变得更加的温和并适应外部工具,它仍然适应 Kotlin 语言。此外,D8 已经产生比 dx 更好的字节码,具有更少的指令和更好的寄存器分配。
这仍然为优化构建过程留下了空间,R8 是 D8 的衍生产品,旨在集成 ProGuard 和 D8 的功能:
R8 将脱糖、压缩、混淆、优化和 dex 处理整合到了一个步骤中,从而显著提升了构建性能。 R8 是在 Android Gradle 插件 3.3.0 中引入的,对于使用插件 3.4.0 及更高版本的应用和 Android 库项目,R8 现已默认处于启用状态。
下图是 R8 引入之前的编译流程的简要概览。
现在,有了 R8,可以在一个步骤中完成脱糖、压缩、混淆、优化和 dex 处理 (D8),如下图所示。
R8 旨在与现有的 ProGuard 规则配合使用,因此可能不需要采取任何操作即可从 R8 中受益。但是,相对专为 Android 项目设计的 ProGuard 而言,R8 是一项不同的技术,因此压缩和优化可能会导致移除 ProGuard 可能没有的代码。因此,在这种情况(尽管不太可能发生)下,可能需要添加其他规则,以在构建输出中保留这些代码。
R8 一步到位地完成了所有的缩减(shrinking),去糖(desugaring)和 转换成 Dalvik 字节码(dex )过程。
缩减(shrinking)过程实现以下三个重要的功能:
- 压缩:从代码中移除无用的类、段、方法等。
- 优化:使代码在指令级更小,更高效。
- 混淆:使用简短无意义的名称重命名代码里剩余的类,字段和方法。
R8 和当前的代码缩减解决方案 Proguard 相比,R8 可以更快地缩减代码,同时改善输出大小。下面将通过几张数据图来对比(数据源自于 benchmark):
三、使用 R8 缩减、混淆处理和优化应用
为了尽可能减小应用的大小,应在发布 build 中启用缩减功能来移除不使用的代码和资源。启用缩减功能后,还会受益于两项功能:
一项是混淆处理功能,该功能会缩短应用的类和成员的名称;
另一项是优化功能,该功能会采用更积极的策略来进一步减小应用的大小。
当使用 Android Gradle 插件 3.4.0 或更高版本构建项目时,该插件不再使用 ProGuard 执行编译时代码优化,而是与 R8 编译器协同工作,处理以下编译时任务:
- 代码缩减(即摇树优化):从应用及其库依赖项中检测并安全地移除不使用的类、字段、方法和属性(这使其成为了一个对于规避 64k 引用限制非常有用的工具)。例如,如果您仅使用某个库依赖项的少数几个 API,那么缩减功能可以识别应用不使用的库代码并仅从应用中移除这部分代码。如需了解详情,请转到介绍如何缩减代码的部分。
- 资源缩减:从封装应用中移除不使用的资源,包括应用库依赖项中不使用的资源。此功能可与代码缩减功能结合使用,这样一来,移除不使用的代码后,也可以安全地移除不再引用的所有资源。如需了解详情,请转到介绍如何缩减资源的部分。
- 混淆:缩短类和成员的名称,从而减小 DEX 文件的大小。如需了解详情,请转到介绍如何对代码进行混淆处理的部分。
- 优化:检查并重写代码,以进一步减小应用的 DEX 文件的大小。例如,如果 R8 检测到某个给定 if/else 语句的
else {}
分支从未被采用过,R8 会移除该else {}
分支的代码。如需了解详情,请转到介绍代码优化的部分。
Gradle插件简述:
Gradle插件是针对Gradle发行版和Android SDK Build Tools封装的一个工具,主要有两大功能:
1)调用Gradle本身的代码和批处理工具来构建项目
2)调用Android SDK的编译、打包功能
Gradle插件版本,在项目主目录下的 build.gradle 中,如com.android.tools.build:gradle:3.6.1
Gradle插件会有版本号,每个版本号又对应有一个或一些 Gradle发行版本(可参考:官方文档:Android Gradle 插件版本说明)
1)启用压缩、混淆和优化功能
当使用 Android Studio 3.4 或 Android Gradle 插件 3.4.0 及更高版本时,R8 是默认编译器,用于将项目的 Java 字节码转换为在 Android 平台上运行的 DEX 格式。不过,当您使用 Android Studio 创建新项目时,缩减、混淆处理和代码优化功能默认处于停用状态。这是因为,这些编译时优化功能会增加项目的构建时间,而且如果您没有充分自定义要保留的代码,还可能会引入错误。
因此,在构建应用的最终版本(也就是在发布应用之前测试的版本)时,最好启用这些编译时任务。如需启用缩减、混淆处理和优化功能,请在项目级 build.gradle
文件中添加以下代码。
默认情况下,在构建应用的发布版本时,R8 会自动执行上述编译时任务。不过,也可以停用某些任务或通过 ProGuard 规则文件自定义 R8 的行为。事实上,R8 支持所有现有 ProGuard 规则文件,因此在更新 Android Gradle 插件以使用 R8 时,无需更改现有规则。
android {
buildTypes {
release {
// Enables code shrinking, obfuscation, and optimization for only
// your project's release build type.
minifyEnabled true
// Enables resource shrinking, which is performed by the
// Android Gradle plugin.
shrinkResources true
// Includes the default ProGuard rules files that are packaged with
// the Android Gradle plugin. To learn more, go to the section about
// R8 configuration files.
proguardFiles getDefaultProguardFile(
'proguard-android-optimize.txt'),
'proguard-rules.pro'
}
}
...
}
更多细节内容建议阅读:官方文档:使用 R8 缩减、混淆处理和优化应用
手动开启方式:只需在项目的 gradle.properties 里加上:
android.enableR8=true
R8 普通模式是兼容 Proguard的,若原项目里已使用了proguard,直接启用 R8 即可。同时,R8 也有完全模式,与Proguard不直接兼容。可以在 gradle.properties 文件中另外设置以下内容:
android.enableR8.fullMode=true
如果在使用 R8 时遇到问题,可以停用 R8,只需将以下其中一行代码添加到项目的 gradle.properties
文件即可:
# Disables R8 for Android Library modules only.
android.enableR8.libraries = false
# Disables R8 for all modules.
android.enableR8 = false
四、ProGuard常用的配置规则
一些更详细的描述可以参考一下下面两篇文章: