周佳:解析 Kotlin 代码检查探索与实践

向作者提问
美团技术团队官方账号
查看本场Chat

2018年8月8日,周三晚20:30,美团点评前端 Android 开发工程师周佳带来了主题为《Kotlin 代码检查探索与实践》的交流。以下是主持人张义整理的问答实录,记录了作者和读者间问答的精彩时刻。


内容提要:

  • KLint 目前只是对隐藏开销行为做了检查吗,还有其他应用吗?
  • Kotlin 项目本身需要的 Kotlin-gradle-plugin 插件也依赖了 Kotlin-Compiler-Embeddable 库,这会与 KLint 插件解析 Kotlin 文件依赖的 Kotlin-Compiler-Embeddable 库产生冲突吗?
  • 除了文中介绍到的 KLint 内容,还有什么可以分享的?
  • 请问作者是用了什么版本的 Kotlin,为什么和我这里看到的 Kotlin反编译之后的结果不一样?
  • 博客文章中遇到的这些坑在 Kotlin native 的时候还存在吗?
  • 过渡到 Kotlin 如何开始,按模块过渡吗?
  • 团队里 Kotlin 会替代 Java 吗?感觉现在 Java 也引入很多函数式的概念?
  • 关于 Kotlin 与 Java 的等效代码之间的性能对比是否有比较过,还有 Kotlin 带来的包体积增大对你们来说影响大吗?

问:KLint 目前只是对隐藏开销行为做了检查吗,还有其他应用吗?

答: KLint 不只可以用来避免开发中的隐藏开销,更可以用来避免踩坑。之前团队在转向 Kotlin 时,使用 AS 自带的Java转Kotlin的工具,遇到过好几次问题,比如将实现了 Parcelable 接口的 Java model 类转成 Kotlin 时,CREATOR 会变成这样:

enter image description here

但转成的这种表达式,CREATOR 其实是 private static final 的,但实际上 CREATOR 必须得是 public static final 类型的。所以,KLint 写了一个检查规则来避免这种情况。比如在 Java 中允许表达式换行,但是在 Kotlin 中因为不存在;号,换行也可能导致一些问题,比如 int a=1\n+2;转成 Kotlin 会变成 val a=1\n+2,但这样 a 的结果就改变了,变成了1而不是3。上面举的这两种情况,在使用 AS 自带的 Java 转 Kotlin 时,不容易发现。我们在实际使用时发现的 Koltin 存在的坑,都会思考如何通过 KLint 来避免。


问:Kotlin 项目本身需要的 Kotlin-gradle-plugin 插件也依赖了 Kotlin-Compiler-Embeddable 库,这会与 KLint 插件解析 Kotlin 文件依赖的 Kotlin-Compiler-Embeddable 库产生冲突吗?

答:第二个问题是会产生冲突,因为 Kotlin 项目本身需要的 Kotlin-gradle-plugin 插件依赖了 Kotlin-Compiler-Embeddable 库,会被 Classloader 先加载进来,而 KLint 插件解析 Kotlin 文件依赖的 Kotlin-Compiler-Embeddable 库由于已经被 Classloader 加载过了,所以不再会加载,这就导致了如果两者 Embeddable 的版本不一致的话,会出现 NoSuchMethodError 的问题。KLint 的解决方案是对 Embeddable 库进行重打包。重打包的 Embeddable 库由于类名修改了,所有的类都会被 Classloader 加载,也就不会出现 NoSuchMethodError 的问题。但是这也导致了一个问题,以后随着 Kotlin 的新特性的出现,目前重打包的 Embeddable 库可能就不支持新语法的解析,需要重新打包。这是一个比较笨的方法。


问:除了文中介绍到的 KLint 内容,还有什么可以分享的?

答: KLint 的博客发完之后,对 KLint 自定义规则的接入方式做了修改。原先 KLint 采用和 Lint 一样的方式去自定义规则(如博客描述),但是这种方式并不友好。第一,自定义规则的工程搭建比较麻烦,相信做过 Lint 自定义规则开发的同学应该都知道。第二,每个业务方实现的 KLint 规则相互独立,业务方之间不知情,存在同一种规则由好几个业务方实现造成的人力的浪费;以及好的规则很难推给其他业务方造成的资源的浪费的问题。

所以 KLint 设置了一套新的自定义 KLint 规则开发的规范,新的规范有如下特点:

  • 中心化管理

所有业务方的自定义规则在一个库中进行注册并开发,由我们来管理所有的 KLint 规则。业务方只需要负责规则的编码工作,规则打包,分发由我们来搞定。

  • 规则共享

在新的规范中,除了业务方注册的规则外,会存在一个 Common 的规则库,Common 规则相当于默认规则,所有接入 KLint 插件的项目都会执行 Common 规则检查。对于业务方制定的好的规则,我们会进行提权,将它放到 Common 规则库中,让所有业务团队共享。

  • 动态下发

无论是 Common 规则还是业务方自己的规则,一旦 pr 合到 master 分支,便由 CI 自动打包。所有 KLint 插件都会立马获取到最新的规则。


问:请问作者是用了什么版本的 Kotlin,为什么和我这里看到的 Kotlin 反编译之后的结果不一样?

答: KLint 插件只用到了 Kotlin 的其中一个 Kotlin-Compiler-Embeddable 库,用的是1.1.2-5的版本。


问:博客文章中遇到的这些坑在 Kotlin Native 的时候还存在吗?

答:目前因为 Embeddable 重打包了,所以 KLint 插件是兼容各个版本的 Kotlin 的。Kotlin Native 和 Kotlin 是不一样的,抽象语法树不同,并且一个编译之后是机器码,一个是字节码。


问:过渡到 Kotlin 如何开始,按模块过渡吗?

答:当时我们团队是按模块过渡,对每个模块进行 Kotlin 语言的替换,得益于 AS 提供的 Java 转 Kotlin 工具,我们这一步做的比较快,但就像第一题的回答,这里面也是踩了一些坑的。上线之后,业务也是很稳定的,并没有带来 crash 的变动。后来,我们接的新的独立的功能也开始使用 Kotlin 去写。直至将业务的所有的代码都替换成 Kotlin。


问:团队里 Kotlin 会替代 Java 吗?感觉现在 Java 也引入很多函数式的概念?

答:我的团队目前已经转向 Kotlin 开发了,除了函数式,Kotlin 还有其他比 Java 优越的地方。目前,美团内部使用 Kotlin 的团队并不多,好像只有个位数。


问:关于 Kotlin 与 Java 的等效代码之间的性能对比是否有比较过,还有 Kotlin 带来的包体积增大对你们来说影响大吗?

答:因为目前并没有数据能用来说明 Kotlin 带来的收益是比 Java 高的。关于 Kotlin 与 Java 的等效代码之间的性能比较可以参考博客以及博客引用到的文章链接。Kotlin 带来的体积相对于整个 APP 的影响并不大。


本文首发于GitChat,未经授权不得转载,转载需要与GitChat联系。

微信扫描登录