拓网科技 2020-11-13
"我们手上是一个很成熟的项目,所以毫无疑问需要保留 Java 代码,目前只会在新开发的页面中使用 Kotlin,并已经感受到了它带来的便利。随着功能的迭代,我们相信更多的功能会转而使用 Kotlin。"
—— 付迎鑫,电信营业厅技术负责人
中国电信电子渠道运营中心,属于中国电信三大主渠道之一,负责电信线上渠道的管理与运营。电信营业厅 app 则是中国电信线上渠道的主要入口。目前该应用的注册用户超过 2 亿,月活跃用户接近 6,000 万,月交易额在 10 亿以上。
电信营业厅应用的技术团队所面临的挑战,基本都和 "速度" 有关:
而 Kotlin 带来的 "加速度",则让开发团队切实感受了一把 "推背感"。
第一脚油门总是需要慎重
2017 年上海,开发团队在谷歌开发者大会上了解到 Kotlin 技术框架已经日趋成熟,在看到很多主流应用纷纷开始使用 Kotlin 之后,团队便决定将 Kotlin 作为重点研究和学习的方向,并开始定期举办内部的学习和分享活动。
△ 电信营业厅技术团队核心成员于谷歌开发者大会。
左起: 付迎鑫 (电信营业厅技术负责人)、刘峻宇 (电信营业厅星播客项目经理)、曾皓 (电信营业厅 Android 项目经理)、张熠 (电信营业厅 Android 项目经理)、黄森 (电信营业厅 Android 开发工程师)
但对开发者们来说,感受一门语言最直接的方式,依然还是上手开发。于是团队决定先在中国电信渠道中心的内部应用 "电渠报表" 中使用 Kotlin 作为主打开发语言。
十分简洁的语法,对 lambda 表达式的支持,以及充分考虑了现代编程需求的架构,让团队 "打开了新世界的大门"。之后 Kotlin 很快就出现在了中国电信渠道中心的各个核心业务中,包括本文开头提到的电信营业厅应用,以及电信星播客应用。
"目前来看,项目整体已经有 20% 在使用 Kotlin,开发新功能的话 50% 的代码都是 Kotlin。"
—— 付迎鑫,电信营业厅技术负责人
△ 用来 "小试牛刀 的电渠报表应用
开得快,开得稳
其实很多时候,"快" 和 "稳" 并不矛盾——疾驰的跑车如果时不时爆胎,那显然也是跑不快的。
空指针异常 (NPE) 导致的崩溃 (或 ANR) 就是让开发者和用户们沮丧的 "爆胎"。Kotlin 可以保护项目避免对可空类型进行误操作。如果类型检测正确,编译器还会进行自动类型转换,NPE 的出现概率降低了 80% 之多,项目稳定性和健壮性显著提高,更为对接和展示各省返回的业务数据打下了良好的基础。
Kotlin 的另一个关键新特性是协程,它是一个轻量级的线程,一个线程中可以创建任意个协程,线程的执行和结束是由操作系统调度的,而协程可以让开发者手动控制其执行和结束。在项目中,团队会结合使用 Retrofit 和协程来处理网络请求,从而更好地控制任务的执行,这样会比单纯使用线程更加节省资源,也更加高效。在使用协程后,团队得以降低首页各个频道接口调用所需的资源,接口调用周期也更加可控。目前电信营业厅的首页和商城等核心页面都是通过 Kotlin 打造出流畅的使用体验。
Kotlin 的 "快" 还体现在对代码的精简上。对于习惯了冗长 Java 代码的 Android 开发者来说,Kotlin 的 SAM (Single Abstract Method) 转换一下子缩减大量的模版代码可能会让他们不太适应——尤其是在通过 lambda 表达式实现 SAM 转换的时候。但更简洁、更有可读性的代码无疑可以让开发者的注意力更容易集中在业务逻辑上,而不是冗长的模版代码上。
另一个能大量精简代码的地方是控件绑定,以前在 Activity 中需要为绑定控件编写大量的代码 (没错,就是 "findViewById")。引入 Jetpack View Binding 之后,ID 可以直接当做变量使用,在 ViewBinding 推出之后,配合 Kotlin 能写出更加安全和简洁的代码。
是时候看看远方的风景了
使用 Kotlin 带来的另一个好处,就是进入 Android 平台新功能的 "快车道"。
比如 Android 10 的时候平台增加了对折叠屏设备的支持,但想要让用户在折叠/展开设备时感觉流畅,免不了需要让应用妥善保存界面状态和支持配置变更,用 Java 编写这种 "保存/读取配置" 的工作虽然可行,但 Kotlin 的 lambda 解构方式能够帮助开发者更加方便地对需要保存的实体类和相关配置进行修改和读取,代码更加精简,可读性也更好。
△ 电信营业厅的折叠态和展开态
在 Android 支持 5G 之后,开发团队可以通过 ConnectivityManager 类拓展的新方法为 5G 用户打造更加快速的网络体验。对那些流量敏感的用例,也可以直接使用网络连接 API 来检测设备是否进行了高带宽连接,并能检查连接是否计费。这时,开发团队大量使用了 Kotlin 的局部委托属性,使代码更加清晰明了。
网络连接 API
https://developer.android.google.cn/reference/android/net/ConnectivityManager
识别非计费状态
https://developer.android.google.cn/reference/android/net/NetworkCapabilities.html#NET_CAPABILITY_NOT_METERED
来自团队的经验分享
从接触、了解 Kotlin,到逐步尝试,乃至正式采用,电信营业厅技术团队总结了一些第一手的经验,这里和大家分享。
"我们会继续加大在 Kotlin 上的投入,目标是在新项目中 100% 使用 Kotlin。"