R8
# Shrink code / code optimize
# 两者在 R8 流程中位置

- input jar
- shrink -> 压缩后的class
- optimize -> 优化后的class
- obfuscate -> 混淆后的class
- preverify -> 混淆后的 jar
- 经过上述步骤产出 output jar
# 共同点
两者都是在编译期间的优化动作,即在静态情况下,能够确认的一些场景,R8 就会帮忙直接给优化掉
# 区别点
- shrink 可以认为是类、方法、变量级别的删除。着重注意关键词"删除"
- 他是在 minifyEnabled=true 后,就会被默认打开的
- 在编译阶段 agp 会根据程序入口(MainActivity)或者 keep 的类,会画出一个方法调用栈的图,如果一个类、类成员变量、方法完全不在这个调用图上,那么他们将会被删除掉
- optimize 主要是指对 code 重写,他是深入方法内部的一种重写。着重注意关键词"重写"
- 他是必须是单独配置才能打开的
- 举例某个 else 一定不会进入,就会被删掉;如果一个方法只有在某几个地方调用使用,便会被内联到对方类中
- 某些方法太简单,比如只是简单做一个 if 判断后,就执行一个赋值、打印等,那这个动作会直接被内联到调用方
# R8 优化详解
# Shrink(代码缩减)
- shrink 可以认为是类、方法、变量级别的删除。着重注意关键词"删除"
- 他是在 minifyEnabled=true 后,就会被默认打开的
- 在编译阶段 agp 会根据程序入口(MainActivity)或者 keep 的类,会画出一个方法调用栈的图,如果一个类、类成员变量、方法完全不在这个调用图上,那么他们将会被删除掉
# 配置示例
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# Keep 规则示例
# 保持某个类不被删除
-keep class com.example.MyClass { *; }
# 保持某个包下的所有类
-keep class com.example.mypackage.** { *; }
# 保持某个类的特定方法
-keepclassmembers class com.example.MyClass {
public void myMethod();
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# Optimize(代码优化)
- optimize 主要是指对 code 重写,他是深入方法内部的一种重写。着重注意关键词"重写"
- 他是必须是单独配置才能打开的
- 举例某个 else 一定不会进入,就会被删掉;如果一个方法只有在某几个地方调用使用,便会被内联到对方类中
- 某些方法太简单,比如只是简单做一个 if 判断后,就执行一个赋值、打印等,那这个动作会直接被内联到调用方
# 配置示例
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
// 启用优化
proguardFiles 'proguard-optimize.pro'
}
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 优化示例
优化前:
class Example { // 注意:这里使用默认访问修饰符,而不是 public
private String name;
void setName(String name) {
if (name != null) {
this.name = name;
}
}
void printName() {
System.out.println(name);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
优化后:
class Example {
private String name;
// setName 方法被内联到调用处
// printName 方法被内联到调用处
}
1
2
3
4
5
6
2
3
4
5
6
注意:类的访问修饰符会影响 R8 的优化行为:
- 使用
public修饰的类和方法更可能被保留- 使用默认访问修饰符(包级私有)的类和方法更容易被优化
- 建议在不需要对外暴露的类和方法上使用默认访问修饰符,以获得更好的优化效果
# 性能提升
- 代码大小:通常可以减少 20-60% 的代码体积
- 启动时间:优化后的应用启动时间通常可以减少 10-30%
- 内存占用:可以减少 10-20% 的内存使用
# 注意事项
调试问题:
- 优化后的代码可能难以调试
- 建议在开发阶段关闭优化
反射调用:
- 使用反射调用的代码需要特别小心
- 必须添加适当的 keep 规则
动态加载:
- 动态加载的类需要特别注意
- 可能需要额外的 keep 规则
性能监控:
- 建议在优化前后进行性能测试
- 监控关键指标的变化
常见问题:
- 类找不到:检查 keep 规则是否完整
- 方法调用失败:检查方法是否被优化掉
- 运行时错误:检查反射调用是否正确处理
# 最佳实践
渐进式优化:
- 先启用 shrink
- 确认无问题后再启用 optimize
规则管理:
- 使用单独的 proguard 文件管理规则
- 定期审查和更新规则
测试策略:
- 在 CI/CD 流程中加入优化测试
- 确保所有功能在优化后正常工作
监控和反馈:
- 收集优化后的性能数据
- 根据反馈调整优化策略
# link
上次更新: 2025/04/29, 11:44:15