在 Android 开发中,很多人对 R8 的印象并不好。
- 构建变慢
- 混淆规则复杂
- 反射代码容易崩溃
于是开发者经常会说:
“R8 太麻烦了。”
但实际上,如果从 编译器架构 的角度来看,R8 并不是一个简单的“混淆工具”,而是一套 完整的代码优化编译器。
它的出现,是 Android 构建系统的一次重要升级。
一、Android 构建工具链的演化
要理解 R8,必须先理解 Android 构建系统的历史。
1 早期 Android 构建流程
最早的 Android 构建链大致如下:
Java / Kotlin
↓
javac
↓
ProGuard
↓
DX
↓
DEX
↓
APK
这里涉及三个关键工具:
| 工具 | 作用 |
|---|---|
| ProGuard | 混淆、代码压缩 |
| DX | 将 class 转换为 dex |
| javac | Java 编译器 |
这种架构存在明显问题:
- 工具过多
- 中间产物多
- 编译效率低
- 优化能力有限
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 容易被反编译 | 重命名类名、方法名、字段名 | UserRepository → a | 提高逆向难度 |
| Dexer | Dex编译 | 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()
混淆的作用:
- 减少包体积
- 增加反编译难度
五、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 的对比
| 能力 | ProGuard | R8 |
|---|---|---|
| 优化能力 | 一般 | 强 |
| 构建速度 | 慢 | 更快 |
| 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 构建系统的一次关键升级。