Kotlin京东业务实战 | 强大又简洁的JVM语言

android studio 下载 | 2018-08-03 06:07

赵振华——京东商城资深工程师

9年以上开发经验,熟悉主流移动开发框架,热衷于探索新技术,曾负责过店铺、JDReact架构等项目的开发工作,目前专注于京东App领券中心频道开发,以及移动端技术难点攻克和新技术调研实施。

2011年JetBrains推出Kotlin项目,这是一个面向JVM的新语言,主要是解决Java之前被诟病已久的问题,而且积极借鉴了 Scala、Ruby等新语言在开发效率和简洁性上的优势。

2017年Google开始力推Kotlin,在I/O大会上谷歌宣布Kotlin正式成为Android官方支持开发语言。

2018年4月18号《JAVA编程思想》作者Bruce大神给6967名京东兄弟分享了kotlin语言,认为kotlin为未来语言发展的趋势之一。

Kotlin已经越走越近,抱着“为了让移动开发更简单”的理念,让Kotlin在京东业务中落地。

简洁:这一点对于Android来说非常重要。项目所需要的库应该尽可能的小。Android对于方法数量有严格的限制,Kotlin依赖库只额外增加了大约6000个方法。同时用Kotlin开发的项目,方法数量也会大幅减少。

安全:Java最大的一个问题就是null。如果没有对变量或是参数进行null判断,那么程序当中就有可能抛出大量的NullPointerException,然而在编码时这些又是难以检测到的。Kotlin使用了显式的null,这会强制我们在必要时进行null检查。

互操作:Kotlin可与Java语言无缝通信。这意味着我们可以在Kotlin代码中使用任何已有的Java库;因此,即便这门语言还很年轻,但却已经可以使用成百上千的库了。除此之外,Kotlin代码还可以为Java代码所用,这意味着我们可以使用这两种语言来构建软件。你可以使用Kotlin开发新特性,同时使用Java实现的其他部分代码。

工具友好:可用任何 Java IDE 或者使用命令行构建,包括常用的IntelliJ IDEA,Android Studio,Eclipse,命令行等。

对比其他语言,Kotlin语法和Java很像,非常容易上手,推荐以循序渐进的方式开发项目;由于项目中允许同时存在Java和Kotlin代码文件,并且允许Java与Kotlin互调,使得开发者可以很方便的在已有项目中引入Kotlin;新模块用Kotlin,稳定模块勿需用Kotlin重写。

Kotlin拥有大量非常打动人心的特性,这里无法一一进行介绍,不过我们来看一下其中最为重要的一些。

如前所述,Kotlin是null安全的。如果一个类型可能为null,那么我们就需要在类型后面加上一个?。这样,每次在使用该类型的变量时,我们都需要进行null检查。比如说,如下代码将无法编译通过:

var artist: Artist? = null?

artist.print()

第2行会显示一个错误,因为没有对变量进行null检查。

Null曾经被戏称为“十亿美金的错误”,Null虽然好用,但是导致很多错误的元凶往往都是它。在Kotlin中,编译器是可以识别你的引用是否是null,进而提醒你。默认kotlin中所有的对象都是不为Null的。

在Java中,如果想要创建数据类或是POJO类(只保存了一些状态的类),我们需要创建一个拥有大量字段、getters与setters的类,也许还要提供toString与equals方法:

public class Artist {

private long id;

private String name;

private String url;

private String mbid;

public long getId() {

public void setId(long id) {

this.id = id;

public String getName() {

return name;

public void setName(String name) {

this.name = name;

public String getUrl() {

return url;

public void setUrl(String url) {

this.url = url;

public String getMbid() {

return mbid;

public void setMbid(String mbid) {

this.mbid = mbid;

@Override public String toString() {

"id=" + id +

", name='" + name + '\'' +

", url='" + url + '\'' +

", mbid='" + mbid + '\'' +

在Kotlin中,上述代码可以写成下面这样:

data class Artist (

var id: Long,

var name: String,

var url: String,

var mbid: String)

使用一行代码创建一个包含 getters、 setters、 equals()、 hashCode()、 toString() 以及 copy() 的 POJO。

Kotlin提供了一些非常棒的互操作特性,这对于Android开发帮助非常大。其中之一就是拥有单个方法的接口与lambda表达式之间的映射。例如下面这个单击监听器:

viewHolder.mCouponItemBottom?.setOnClickListener(

object : View.OnClickListener {

override fun onClick(v: View) {

Toast.makeText(mContext, "Click", Toast.LENGTH_LONG).show()

可以写成这样:

viewHolder.mCouponItemBottom?.setOnClickListener {

Toast.makeText(mContext, "Click", Toast.LENGTH_LONG).show()

Lambda表达式会极大程度的精简代码,借助于Lambda表达式,我们可以做到之前无法实现或是实现起来非常麻烦的事情。借助于Lambda表达式,我们可以以一种更加函数式的方式来思考问题。Lambda表达式其实就是一种指定类型,并且该类型定义了一个函数的方式。

lambda的标准形式基本声明满足三个条件:含有实际参数,含有函数体,以上内部必须被包含在花括号内部。

val sum = { x: Int, y: Int -> x + y }

Kotlin代码比Java的简洁,更易于编写维护,所以我们认为转换是值得的。 但很多开发者都担心Kotlin编译可能没有Java快,影响开发效率,反而得不偿失。

上图是Java编译器的编译过程,Kotlin和Java的编译过程是很相似的,区别在于Kotlin与Java相比重要的细节在编译后端(目标代码生成)环节。

Kotlin编译器在目标代码生成环节做了很多类似于Java封装的事情,比如自动生成Getter/Setter代码的生成、Companion转变成静态类、修改类属性为final不可继承(open修饰即可继承)等等工作。

Kotlin将我们本来在代码层做的一些封装工作转移到了编译后端阶段,使得语言更加简洁。

在相同gradle版本,相同设备的情况下,通过重复执行gradle指令,对几个不同的编译场景进行了基准测试,对比Kotlin和Java的编译时间。发现Java在clean构建比Kotlin 快10-15%,增量编译时Kotlin比Java编译速度略快。对于大多数开发人员来说,更常见的情况是增量编译,Kotlin对增量编译进行了大量改进,保证了编译速度。

由此可见,开发人员不需要担心Kotlin的编译时间,Kotlin的编译速度和Java一样快。

1、安装 Kotlin 插件

Android Studio 从3.0(preview)版本开始将内置安装 Kotlin插件。如果你正在使用的是早期版本, 需要通过File | Settings | Plugins | Install JetBrains plugin…搜索并安装Kotlin插件。

2、Jdlib工程中配置 Kotlin

新增 apply plugin: ‘kotlin-android’ 及其依赖。

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"

dependencies {

classpath "org.jetbrains.kotlin:kotlin-android-extensions:1.2.41"

dependencies {

classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

3、插件工程中配置 Kotlin

配置方式与Jdlib相同,注意需要将compile修改为provided,防止类库重复引用。如果不配置,插件代码不能打到apk中,调用时报ClassNotFoundException异常。

provided "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"

4、添加混淆配置

如果调试Kotlin代码,总是报ClassNotFoundException异常,或者NoSuchMethod异常,你要考虑加入混淆配置。缺失混淆配置,Kotlin类库代码会被优化掉,编译正常,但无法运行。

-dontwarn kotlin.**

-keep class kotlin.** {*;}

-keepclassmembers class **$WhenMappings {

<fields>;

<methods>;

将整个模块改为Kotlin语言实现,修复语法方面的编译错误。Android Studio提供将Java转为Kotlin的插件,可以转换大部分代码。

常见编译问题:

a、代码编译出错,定义变量时,没有赋初始值,变量可以为空,需要加?修饰符

var mCouponItemLayout: RelativeLayout

var mCouponItemTop: LinearLayout

var mCouponIcon: SimpleDraweeView

var mCouponName: TextView

var mCouponItemTopTv1: TextView

var mCouponItemTopTv2: TextView

var mCouponItemMiddle: LinearLayout

以下为修改后的代码:

var mCouponItemLayout: RelativeLayout? = null

var mCouponItemTop: LinearLayout? = null

var mCouponIcon: SimpleDraweeView? = null

var mCouponName: TextView? = null

var mCouponItemTopTv1: TextView? = null

var mCouponItemTopTv2: TextView? = null

var mCouponItemMiddle: LinearLayout? = null

b、代码编译出错,变量可以为空时,使用变量需要加?修饰符,自动判断是否为空,防止出现空指针异常

viewHolder.mCouponItemValueDiscount.text = entity.quota

viewHolder.mCouponItemValuePrice.text = entity.minOrderAmount

以下为修改后的代码:

viewHolder.mCouponItemValueDiscount?.text = entity.quota

viewHolder.mCouponItemValuePrice?.text = entity.minOrderAmount

c、代码编译出错,由于FontsUtil的参数有@NonNull标签,需要保证只有在确保变量不是null的情况下才能这么调用,否则它会抛出异常

FontsUtil.changeTextFont(viewHolder.mCouponItemMoneyTag, FontsUtil.MULTI_BOLD)

FontsUtil.changeTextFont(viewHolder.mCouponItemValueDiscount, FontsUtil.MULTI_REGULAR)

以下为修改后的代码:

FontsUtil.changeTextFont(viewHolder.mCouponItemMoneyTag!!, FontsUtil.MULTI_BOLD)

FontsUtil.changeTextFont(viewHolder.mCouponItemValueDiscount!!, FontsUtil.MULTI_REGULAR)

编译成功后进行代码调试,修改运行时异常问题,可以正常使用debug工具,Kotlin模块与Java模块互相直接调用,显示效果和交互效果与Java模块没有差别。

Kotlin语言提供了类型的自动判断,自动拆装箱,字符串拼接,lambda表达式,空判断等一系列功能,功能精简了很多,语法与js有相似,同时去掉了findViewById(),省去了很多if try等语句,业务代码量减少很多。

统计业务模块的Java实现和Kotlin实现的代码量,不包含xml布局文件,代码量减少超过20%;代码减少最多的为pojo类,减少比例甚至超过80%;业务逻辑代码减少10%,同时代码会更加简洁直观,有助于提高代码可维护性。

由于业务量级比较大,为防止新技术对业务稳定性产生影响,计划通过Java实现代码,Kotlin 实现代码两套代码并存,使用ABTest方式逐渐放量,待稳定后切到Kotlin。

Kotlin在设计上避免了常见的编程错误,从而减少了应用程序崩溃和系统故障。此外,由于Kotlin 是快速失败机制,可以立即报告任何可能导致失败的问题。因此 Kotlin 在降低应用崩溃率上有很大作用,非常值得期待!

环境配置出错,出现无法编译或编译正常但apk中无kotlin代码,运行时报kotlin代码找不到; 解决方法:在插件代码中配置kotlin环境,在gradle中增加classpath,compile等,如果不配置环境,kotlin代码不会做编译;

正常出包后,无法运行,调试kotlin代码,总是报Intrinsics的ClassNotFoundException异常,通过反编译分析apk,发现丢失部分代码; 解决方法:在jdlib代码中,加入遗漏的kotlin-android-extensions依赖,同时修改proguard,解决ClassNotFoundException异常;

运行过程中报checkExpressionValueIsNotNull NoSuchMethod异常,分析apk与kotlin源码,发现kotlin中Intrinsics类部分方法打包后丢失; 解决方法:在主站代码中配置属性方法混淆,保证kotlin类中属性方法全部不做混淆优化;

为了保证插件包大小,并且与主站代码不重复,引用类库时使用provided; 解决方法:使用provided引入类库,在编译时使用,最终不会被编译到apk;后期将配置环境放到aura中更方便。

Pinterest 已成功将 Kotlin 引入了他们的应用程序,每个月有 1 亿 5 千万人使用。

Gradle 正引入 Kotlin 作为编写脚本的语言。

Evernote 最近 将 Kotlin 整合到了他们的 Android 客户端。

Uber 团队使用 Kotlin 来构建内部工具。

Corda是一个开源分布式分类账号平台,由各大银行提供支持,完全由 Kotlin 构建。

Coursera Android 应用程序部分用 Kotlin 编写。

Spring 采用 Kotlin 的语言特性来提供更简洁的 API。

Trello Android应用程序中的所有新代码都用 Kotlin。

对话《JAVA编程思想》作者Bruce 地址:

---------------------END---------------------

下面的内容同样精彩