Kotlin Vocabulary | 枚举和 R8 编译器
编译器
1. Kotlin 编译器
2. D8
3. R8 (可选,但推荐使用)
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile(
‘proguard-android-optimize.txt’),
‘proguard-rules.pro’
}
}
}
D8
https://developer.android.google.cn/studio/command-line/d8
R8
https://developer.android.google.cn/studio/build/shrink-code
枚举
enum class BlendMode {
OPAQUE,
TRANSPARENT,
FADE,
ADD
}
这个枚举中包含四个值。这些值是什么无关紧要,这里仅作为示例。
枚举 + when
接下来,我们使用一个 when 语句来转换这个枚举:
fun blend(b: BlendMode) {
when (b) {
BlendMode.OPAQUE -> src()
BlendMode.TRANSPARENT -> srcOver()
BlendMode.FADE -> srcOver()
BlendMode.ADD -> add()
}
}
对应枚举的每一个值,我们都去调用另一个方法。
如果您去看这段代码编译成的 Java 字节码 (您可以通过 Android Studio 的查看字节码功能直接看到 (Tools -> Kotlin -> Show Kotlin Bytecode),然后点击 "Decompile" 按钮),就会看到下面这样的代码:
public static void blend(@NotNull BlendMode b) {
switch (BlendingKt$WhenMappings.
$EnumSwitchMapping$0[b.ordinal()]) {
case 1: {
src();
break;
}
// ...
}
}
这段代码中没有对枚举直接使用 switch 语句,而是调用了一个数组。这个数组是从哪来的呢?
而且这个数组存储在一个被生成的类文件中。这个类文件是从哪来的?
这里究竟发生了什么呢?
自动生成的枚举映射
事实上,为了实现二进制兼容,我们不能简单地依靠枚举的序数值进行转换,因为这样的代码十分脆弱。假设您的一个库中包含了一个枚举,而您改变了这个枚举中值的顺序,您就可能破坏了某个人的应用。虽然这些代码除了顺序,看起来完全相同,但就是这种顺序的不同导致了对其它代码的影响。
public final class BlendingKt$WhenMappings {
public static final int[] $EnumSwitchMapping$0 =
new int[BlendMode.values().length];
static {
$EnumSwitchMapping$0[BlendMode.OPAQUE.ordinal()] = 1;
$EnumSwitchMapping$0[BlendMode.TRANSPARENT.ordinal()] = 2;
$EnumSwitchMapping$0[BlendMode.FADE.ordinal()] = 3;
$EnumSwitchMapping$0[BlendMode.ADD.ordinal()] = 4;
}
}
这段代码中生成了一个 BlendingKt$WhenMappings 类。这个类里面有一个存储映射信息的数组: $EnumSwitchMapping$0,接下来则是一些执行映射操作的静态代码。
示例中是只有一个 when 语句时的情况。但如果我们写了更多的 when 语句,每个 when 语句就会生成一个对应的数组,即使这些 when 语句都在使用同一个枚举也一样。
虽然所有这些开销没什么大不了的,但是却也意味着,在您不知情的时候,会生成一个类,而且其中还包含了一些数组,这些都会让类加载和实例化消耗更多的时间。
幸运的是,我们可以做一些事情来减少开销: 这就是 R8 发挥作用的时候了。
使用 R8 来解决问题
public static void blend(@NotNull BlendMode b) {
switch (b.ordinal()) {
case 0: {
src();
break;
}
// ...
}
}
这样就避免了生成类和映射数组,而且只创建了您所需的最佳代码。
探索 R8 与 Kotlin,然后用 Kotlin 写出更好的应用吧。
更多信息
官方文档 | D8 https://developer.android.google.cn/studio/command-line/d8 官方文档 | 缩减、混淆、优化您的应用 https://developer.android.google.cn/studio/build/shrink-code Jake Wharton 的博客,详细介绍了 D8 和 R8 的工作原理,并为各种功能提供了示例,以及如何直接运行编译器、如何获得反编译的结果等 https://jakewharton.com/blog/
推荐阅读