assumevalues 用法
assumevalues 告知 r8 指定的字段值或者方法值将始终处于特定范围内或等于某个单一值
先看一段简单的代码
kotlin
class Count {
public static void main(String... args) {
count = 3;
sayHi();
}
private static int count = 1;
private static void sayHi() {
if (count < 0) {
throw new IllegalStateException();
}
for (int i = 0; i < count; i++) {
System.out.println("Hi!");
}
}
}
混淆前

混淆后, 不但做了内联,还帮我们做了一定程度上的剪枝处理
再看下我们常用的 android 版本判断
kotlin
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
if (Build.VERSION.SDK_INT >= 32) {
println("32+++")
} else if (Build.VERSION.SDK_INT >= 31) {
println("31")
} else {
println("<31")
}
}
}

假设我们的 min sdk 是 31,
混淆代码以后,代码没有发生什么变化

但是在 proguard 文件中 加入一段代码,因为我们明确知道 我们最小 min sdk 是 31 的
kotlin
-assumevalues class android.os.Build$VERSION {
int SDK_INT return 31..2147483647;
}
再看 r8 混淆后的效果, 嗯这次就符合预期了

注意我们 androidx 里面有大量的 android 版本判断,最低是到 14,所以如果我们可以根据我们项目实际的效果配置这个对应的assumevalues,可以节约不少代码,提高程序执行效率
agp>7.0的版本中我实测是不需要自己手动写Build$VERSION 的assumevalues了,agp 和 r8 会自动配合处理好,这里只是演示一下这个 proguard 写法的作用。
对于部分经常做sdk 的同学来说,在你的 sdk 接入文档中 ,写好对应的assumevalues 也是有助于你 sdk 的性能的,大部分做 app 的知道有这么个东西即可
字符串常量 与类常量
字符串常量的优化 相对而言就不那么重要了,主要是这部分的优化一般情况下 不会引发什么错误,至少我目前还没碰到过 因为字符串常量优化 而导致的运行时 crash 或者 build 失败,大家知道有这么个东西即可
kotlin
static String patternHost(String pattern) {
return pattern.startsWith(WILDCARD)
? pattern.substring(WILDCARD.length())
: pattern;
}
例如上面这段代码,WILDCARD.length() 在 r8 处理之后 会变成一个常量值,避免 程序运行时再去常量池里去找 这里就不具体演示优化前和优化后的字节码了,大家自行体会即可



不过这里大家可能会疑惑,如果不是 activity,是个普通类,还能这样写吗? 按照jw 的原文
的表述,普通类也可以享受此优化,因为 混淆和优化 都是 r8来控制,r8 完全可以做到 先混淆,再优化 这样就可以保证 getSimpleName 方法拿到的值是混淆后的类名。
我这里实验起来的方案来看,当前 r8 版本应该没有做类似的优化了
这里有知道原因的大佬可以评论区里说下原因。

个人猜测,可能是我用于测试的类写的过于简单 导致r8 优化的时候 把我这个类当做是抽象类处理,对于抽象类来说是无法享受此优化的