阅读 398

Kotlin: 发布 Release 包前优化 Java 字节码

Kotlin 中删除了大量需要我们手写或者由 IDE 、第三方框架生成的模板代码。最好的例子就是 Kotlin 是空安全的,由于这个特性,我们不需要写空判断语句 if(value != null) ,这是 Kotlin/JVM 中额外添加在 Java 字节码中实现的。所以说好的功能必然需要付出一定的代价来换取。

下面让我们一起来看看 Kotlin 编译后的字节码,并学习如何降低这种代价。

函数参数

示例代码:

// Sample.kt
fun sample(arg1: String) {
}
复制代码

编译后的 Java 字节码:

public final class SampleKt { 
     public static final void sample(@NotNull String arg1) {
        Intrinsics.checkParameterIsNotNull(arg1, “arg1”);
     }
}
复制代码

在上面的字节码中,可以看到调用了 kotlin.jvm.internal.Intrinsics 类中的 checkParameterIsNotNull 方法进行空检查。但是实际上我们是否都需要这些代码呢?让我们看下另一个例子。

平台类型

Kotlin 中有特定的 types—[platform type](http://platform type/)。你不能声明这些类型,只能通过其它代码返回。

// Utils.java
public class Utils {

    public static String getValue() {
        throw new RuntimeException();
    }
}
// Sample.kt
fun sample() {
    val value: String = Utils.getValue()
}
复制代码

编译后的字节码:

public final class SampleKt {
   public static final void sample() {
      Intrinsics.checkExpressionValueIsNotNull(
         Utils.getValue(), "Utils.getValue()"
      );
   }
}
复制代码

可以看到字节码非常精简,如果我们能将开发期间写的调试代码在 Release 版本中进行移除,这个特性肯定很实用。

移除代码

ProGuard/R8

你可以在你的 ProGuard 文件中添加下面的混淆配置来移除工具类的字节码。

// proguard.pro
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
    public static void checkExpressionValueIsNotNull(...);
    public static void checkNotNullExpressionValue(...);
    public static void checkReturnedValueIsNotNull(...);
    public static void checkFieldIsNotNull(...);
    public static void checkParameterIsNotNull(...);
}
复制代码

-assumenosideeffects 需要你自己保证你所选择的类的方法没有边界效应(简单来说就是删掉也不会影响程序运行),然后 proguard 会帮你删掉这些方法的调用。

Kotlin 编译参数

通过 kotlin 编译参数配置可以删除一些 assert

  • -Xno-call-assertions:不对 platform types 的参数生成空检查
  • -Xno-receiver-assertions:不对 platform types 的接收参数生成空检查
  • -Xno-param-assertions:不对 Java 方法参数生成非空检查
// build.gradle
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile)
    .all {
        kotlinOptions {
            freeCompilerArgs += [
                    '-Xno-call-assertions',
                    '-Xno-receiver-assertions',
                    '-Xno-param-assertions'
            ]
        }
    }
复制代码

你可以在这里查看更多关于 Kotlin/JVM 的编译参数信息。

总结

Kotlin 是一门很棒的开发语言,尽管它的一些特性支持需要一些代价。但是那些说 Java 没有添加太多额外字节码的操作,而 Kotlin 添加很多的说法是愚蠢的。例如 Java 中的特性(泛型和 switch 支持 String)都是通过生成其它的字节码实现的。Kotlin 生成更多的附加字节码,因此您在源代码中写的更少。

同时,你可以通过 ProGuard/R8 降低这些影响。我建议在发布版本时使用这两种方法。为什么只针对发布版本?因为高质量的 Android 应用程序必须速度快,每秒 60 帧,而且体积小。但仅限于终端用户,不要从调试版本中删除任何内容,以获得尽可能多的有关应用程序错误行为的信息,从而简化应用程序的调试。

关注下面的标签,发现更多相似文章
评论