Android R8 深度解析:为什么 Google 用R8取代 ProGuard?

15 阅读5分钟

在 Android 开发中,很多人对 R8 的印象并不好。

  • 构建变慢
  • 混淆规则复杂
  • 反射代码容易崩溃

于是开发者经常会说:

“R8 太麻烦了。”

但实际上,如果从 编译器架构 的角度来看,R8 并不是一个简单的“混淆工具”,而是一套 完整的代码优化编译器

它的出现,是 Android 构建系统的一次重要升级。


一、Android 构建工具链的演化

要理解 R8,必须先理解 Android 构建系统的历史。


1 早期 Android 构建流程

最早的 Android 构建链大致如下:

Java / Kotlin
     ↓
javac
     ↓
ProGuard
     ↓
DX
     ↓
DEX
     ↓
APK

这里涉及三个关键工具:

工具作用
ProGuard混淆、代码压缩
DX将 class 转换为 dex
javacJava 编译器

这种架构存在明显问题:

  1. 工具过多
  2. 中间产物多
  3. 编译效率低
  4. 优化能力有限

2 Android 新构建流程

后来 Google 推出了新的工具链:

Java / Kotlin
      ↓
R8
      ↓
DEX
      ↓
APK

R8 实际上整合了多个功能模块:

Desugar
Shrinker
Optimizer
Obfuscator
Dexer

这里涉及五个关键工具:

模块作用解决的问题工作原理典型示例对工程的价值
Desugar语法降级低版本 Android 不支持 Java8 / Kotlin 新特性将高级语法转换为旧版 JVM 字节码Lambda → 匿名内部类兼容低版本 Android
Shrinker代码裁剪第三方库代码过多导致 APK 体积过大构建调用图,删除未被引用的类/方法/字段删除未使用的 Library 类APK 体积减少
Optimizer代码优化Java/Kotlin 编译代码不是最优IR优化:内联、常量传播、死代码删除add(1,2)1+2提升运行效率
Obfuscator混淆APK 容易被反编译重命名类名、方法名、字段名UserRepositorya提高逆向难度
DexerDex编译Android 不运行 Java class.class → .dex 转换Java Bytecode → Dex生成 Android 可执行代码

换句话说:

R8 = ProGuard + D8 + 编译优化器

其中 D8 是 Android 新一代 Dex 编译器。

从 **Android Studio 3.4 开始,R8 成为默认工具。


二、R8 的核心架构

R8 的本质是一个 全程序优化编译器(Whole Program Optimizer)

其核心流程如下:

读取class
      ↓
构建调用图
      ↓
代码裁剪
      ↓
IR优化
      ↓
混淆
      ↓
生成Dex

核心模块包括:

模块作用
Liveness Analysis存活分析
Tree Shaking删除无用代码
IR Optimization代码优化
Obfuscation混淆
Dex Generation生成 dex

三、R8 的关键算法

1 存活分析(Liveness Analysis)

R8 会分析程序中哪些代码真正被使用。

例如:

fun main(){
    test()
}

fun test(){
    println("hello")
}

fun useless(){
}

调用关系:

main →test

useless() 没有被引用。

最终结果:

useless() 被删除

这一步是 代码压缩的核心基础


2 Tree Shaking(代码裁剪)

Tree Shaking 的作用是删除:

  • 未使用 class
  • 未使用 method
  • 未使用 field

例如:

Library
 ├── ClassA
 ├── ClassB
 └── ClassC

如果项目只用到 ClassA

ClassB / ClassC 会被删除

这可以显著减少 APK 体积。


3 IR(中间表示)优化

R8 会把 Java 字节码转换为 IR(Intermediate Representation)

Java Bytecode
      ↓
Intermediate Representation
      ↓
Dex Bytecode

IR 允许进行更多高级优化。

常见优化

方法内联

原代码

int add(int a,int b){
	return a+b;
}

调用:

x = add(1,2)

优化后:

x =1 +2

常量传播

原代码:

int a =5int b = a +3

优化:

int b =8

删除冗余逻辑

原代码:

if(false){
	doSomething()
}

优化:

直接删除

四、代码混淆(Obfuscation)

R8 也会执行混淆操作。

原代码:

UserRepositorygetUserName()

混淆后:

ab()

混淆的作用:

  1. 减少包体积
  2. 增加反编译难度

五、Dex 生成阶段

Android 运行的是 DEX 字节码

R8 在最后阶段会生成 dex 文件:

.class
   ↓
.dex

这个过程内部调用的是 D8 Dex 编译器


六、为什么开发者讨厌 R8

虽然 R8 技术先进,但很多开发者依然讨厌它。

主要原因有三个。


1 Keep 规则复杂

如果代码依赖反射:

Class.forName("UserModel")

R8 无法检测到引用关系。

必须添加规则:

-keepclasscom.xxx.model.** { *; }

否则代码会被删除。


2 反射框架容易崩

例如以下框架:

  • Retrofit
  • Glide
  • Gson

都依赖反射。

如果规则不完整:

运行时崩溃

3 第三方库规则复杂

很多库都需要配置混淆规则,例如:

-keepclassretrofit2.** { *; }

否则接口解析会失败。


七、R8 的真正价值

很多人以为 R8 只是混淆工具。

其实它最大的价值是:

编译器级优化。

例如一个大型应用:

200000+methods

R8 可以:

  • 删除未使用代码
  • 优化方法调用
  • 减少对象创建

实际工程中可能带来:

指标变化
APK体积减少 10%~20%
启动速度提升
ANR概率下降

八、R8 与 ProGuard 的对比

能力ProGuardR8
优化能力一般
构建速度更快
Dex支持不支持支持
全程序优化不支持支持
默认工具

因此 Google 最终选择 R8 作为默认方案。


九、构建系统未来趋势

Android 构建系统正在向 工具链融合 发展。

过去:

javac
dx
proguard
desugar

现在:

R8

未来趋势可能是:

Kotlin IR
   ↓
R8
   ↓
DEX

构建流程会越来越简单。


十、Android 开发者必须掌握的 R8 能力

如果你在维护中大型 Android 项目,建议掌握以下内容:

1️⃣ Keep 规则设计

2️⃣ Mapping 文件分析

3️⃣ shrinkResources 使用

4️⃣ R8 full mode

5️⃣ Baseline Profile 优化


总结

R8 本质不是混淆工具,而是一个 完整的 Android 编译优化器

R8 = 编译器 + 优化器 + 混淆器 + Dex生成器

实现了 构建效率提升与 APK 体积优化

从 Android 架构演进来看,R8 是 Android 构建系统的一次关键升级。