跳到主要内容

R8 与代码混淆

问题

R8 和 ProGuard 是什么关系?混淆规则如何配置?

答案

R8 vs ProGuard

特性ProGuardR8
开发者GuardSquareGoogle
功能缩减 + 混淆 + 优化缩减 + 混淆 + 优化 + 脱糖
性能较慢更快(单 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)通过反射读取字段名,混淆后字段名变为 ab 等,导致反序列化失败。解决方案:

  1. 使用 @SerializedName 注解 + 对应 keep 规则
  2. 使用 Kotlin Serialization(编译期生成序列化代码,不依赖反射,天然抗混淆)
  3. 使用 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

相关链接