R8 与代码混淆
问题
R8 和 ProGuard 是什么关系?混淆规则如何配置?
答案
R8 vs ProGuard
| 特性 | ProGuard | R8 |
|---|---|---|
| 开发者 | GuardSquare | |
| 功能 | 缩减 + 混淆 + 优化 | 缩减 + 混淆 + 优化 + 脱糖 |
| 性能 | 较慢 | 更快(单 Pass) |
| 兼容性 | 兼容 ProGuard 规则 | 兼容 ProGuard 规则 |
| AGP 默认 | AGP 3.4 之前 | AGP 3.4+ 默认 |
信息
AGP 7.0+ 已移除 ProGuard,只能使用 R8。R8 兼容 ProGuard 的规则语法。
R8 处理流程
开启混淆
app/build.gradle.kts
android {
buildTypes {
release {
isMinifyEnabled = true // 开启代码缩减和混淆
isShrinkResources = true // 开启资源缩减
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
}
常用混淆规则
proguard-rules.pro
# 保留实体类(JSON 序列化需要字段名)
-keep class com.example.model.** { *; }
# 保留特定注解的类
-keep @com.google.gson.annotations.SerializedName class * { *; }
-keepclassmembers class * {
@com.google.gson.annotations.SerializedName <fields>;
}
# 保留枚举
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# 保留 Parcelable
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
# 保留 native 方法
-keepclasseswithmembernames class * {
native <methods>;
}
# 保留 View 构造函数(XML 中引用的自定义 View)
-keep public class * extends android.view.View {
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
# 保留 WebView JavaScript 接口
-keepclassmembers class * {
@android.webkit.JavascriptInterface <methods>;
}
资源缩减
res/raw/keep.xml
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@layout/l_used*,@drawable/ic_keep*"
tools:discard="@layout/unused_layout" />
映射文件
混淆后 R8 生成 mapping.txt,用于还原堆栈信息:
# 使用 retrace 还原混淆堆栈
# AGP 自带 retrace 工具
java -jar retrace.jar mapping.txt stacktrace.txt
注意
发布每个版本时务必保存 mapping.txt,否则线上崩溃无法还原。Firebase Crashlytics 等工具支持自动上传 mapping 文件。
常见面试问题
Q1: 为什么实体类(data class)通常需要 keep?
答案:
R8 会将未直接引用的字段和类重命名或移除。JSON 序列化库(Gson、Moshi)通过反射读取字段名,混淆后字段名变为 a、b 等,导致反序列化失败。解决方案:
- 使用
@SerializedName注解 + 对应 keep 规则 - 使用 Kotlin Serialization(编译期生成序列化代码,不依赖反射,天然抗混淆)
- 使用 Moshi codegen(编译期生成 Adapter)
Q2: R8 Full Mode 是什么?
答案:
R8 Full Mode(AGP 8.0+ 默认开启)比兼容模式更激进:
- 更多的 Tree Shaking:移除未使用的类层次
- 更多的优化:参数移除、返回值优化
- 不保留
-keepattributes中的某些默认属性
Full Mode 可能导致更多的兼容性问题(如反射调用失败),但产物体积更小。如遇问题,可添加对应 keep 规则或回退到兼容模式:
gradle.properties
android.enableR8.fullMode=false