深圳幻海软件技术有限公司 欢迎您!

Android 中的 R8详解

2023-03-13

Google发布了R8来作为Proguard的替代品,用以帮助开发人员通过生成更好的输出(APK)来缩减代码。与Proguard相比,R8被认为要比Proguard要快得多。1、什么是R8?R8是一个将我们的java字节码转换为优化的dex码的工具。它遍历整个应用程序,然后对其进行优化,例如删除未使

Google 发布了 R8 来作为 Proguard 的替代品,用以帮助开发人员通过生成更好的输出 (APK) 来缩减代码。与 Proguard 相比,R8 被认为要比Proguard要快得多。

1、什么是R8?

  • R8 是一个将我们的 java 字节码转换为优化的 dex 码的工具。
  • 它遍历整个应用程序,然后对其进行优化,例如删除未使用的类、方法等。
  • 它在编译时运行。它可以帮助我们减少构建的大小并使我们的应用程序更加安全。
  • R8 使用 Proguard 规则来修改其默认行为。

(1)开启R8的好处

  • 代码缩减(摇树优化):使用静态代码分析来查找和删除无法访问的代码和未实例化的类型,对规避 64k 引用限制非常有用。
  • 资源缩减:移除不使用的资源,包括应用库依赖项中不使用的资源。
  • 混淆代码:缩短类和成员的名称,从而减小 DEX 文件的大小
  • 优化代码:检查并重写代码,选择性内联,移除未使用的参数和类合并来优化代码大小.
  • 减少调试信息 : 规范化调试信息并压缩行号信息
  • R8 会自动执行上述编译时任务,也可以停用某些任务或通过 ProGuard 规则文件自定义 R8 的行为。
  • 使用某个第三方库时,通常只使用其中很小一部分。若不压缩,所有库代码都会保留在应用中。冗长的代码有时可以提高可读性和可维护性。

2、R8 怎么用

(1)R8 编译器主要作用有:

  • 代码缩减(即摇树优化)从应用及其库依赖项中检测并安全地移除不使用的类、字段、方法和属性(这使其成为了一个对于规避 64k 引用限制非常有用的工具)。例如,如果你仅使用某个库依赖项的少数几个 API,那么缩减功能可以识别应用不使用的库代码并仅从应用中移除这部分代码。
  • 资源缩减:从封装应用中移除不使用的资源,包括应用库依赖项中不使用的资源。此功能可与代码缩减功能结合使用,这样一来,移除不使用的代码后,也可以安全地移除不再引用的所有资源。
  • 优化:检查并重写代码,以进一步减小应用的 DEX 文件的大小。例如,如果 R8 检测到从未采用过给定 if/else 语句的 else {} 分支,则会移除 else {} 分支的代码。
  • 混淆:使用无意义的简短名称重命名类、方法和字段,增加逆向难度,并且缩短类和成员的名称,从而减小 DEX 文件的大小。

(2)启用与禁用 R8

如需启用缩减、混淆处理和优化功能,请在项目级 build.gradle 文件中添加以下代码:

android {
    buildTypes {
        release {
            // Enables code shrinking, obfuscation, and optimization for only
            // your project's release build type.
//启用 R8 的代码缩减功能
            minifyEnabled true
            // Enables resource shrinking, which is performed by the
            // Android Gradle plugin.
     //启用 R8 的资源缩减功能
            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'
        }
    }
    ...
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

关闭R8,gradle.properties中添加配置

# 显式启用 R8
android.enableR8 = true
# 只对 Android Library module 停用 R8 编译器
android.enableR8.libraries = false
# 对所有 module 停用 R8 编译器
android.enableR8 = false
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

R8 普通模式是兼容 Proguard 的,若原项目里已使用了 Proguard,直接启用 R8 即可。同时,R8 也有完全模式,但是与 Proguard 不直接兼容,可以在 gradle.properties 文件中另外设置以下内容开启:

android.enableR8.fullMode=true
  • 1.

额外的优化功能会使 R8 的行为与 ProGuard 不同,因此可能会需要您添加额外的 ProGuard 规则,以避免运行时问题

(3)自定义要保留的代码

强制 R8 保留某些代码,在 ProGuard 规则文件中添加 -keep 代码行,如

-keep public class MyClass
  • 1.

为要保留的代码添加 @Keep 注解

  • 在类上添加 @Keep 可按原样保留整个类。
  •  在方法或字段上添加该注释,将使该方法/字段(及其名称)以及类名称保持不变。
  • 只有在使用 AndroidX 注解库且您添加 Android Gradle 插件随附的 ProGuard 规则文件时,此注解才可用。

3、R8 与 Proguard 的比较

  • 对于使用 3.4.0 或更高版本的 Gradle 插件的 Android 应用,项目默认使用R8,不再使用Proguard进行优化。但是,它只使用 Proguard 规则。
  • R8 有效地内联容器类并删除未使用的类、字段和方法。Proguard 将应用程序大小减少了 8.5%,与 R8 相比减少了 10% 的代码.
  • 与 Proguard 相比,R8 对 Kotlin 的支持更多。
  • R8 提供比 Proguard 更好的输出,并且比 Proguard 更快,从而减少了整体构建时间。

(1)比较一下 Proguard 和 R8的性能

Proguard:

在使用 Proguard 时,应用程序代码由 Java 编译器转换为 Java 字节码。转换后,Proguard 使用我们编写的规则对其进行优化。然后 dex 将其转换为优化的 Dalvik 字节码。

将其转换为 Dalvik 字节码大概需要 4 个步骤。

R8:

  • 在使用 R8 时,首先将应用程序的代码通过 java 编译器转换为 Java 字节码,然后直接使用 R8 将 Java 字节码转换为 Dalvik 字节码。
  • 通过使用 R8,它直接将 Java 字节码转换为 Dalvik 字节码的步骤从 2 减少到 1。
  • 与 R8 相比,Proguard 应用了 520 个窥孔优化,后者非常少。窥孔优化是对一组编译器生成的代码执行的优化,通过使代码更短、更快来提高代码的性能。
  • 在 Proguard 和 R8 中,我们必须通过编写自定义配置来处理反射。
  • 在转换代码的执行上,R8 比 Proguard 更快。