赵振华——京东商城资深工程师
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在京东业务中落地。
对比其他语言,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() {
return id;
}
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() {
return "Artist{" +
"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类库代码会被优化掉,编译正常,但无法运行。
# 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 在降低应用崩溃率上有很大作用,非常值得期待!
Pinterest 已成功将 Kotlin 引入了他们的应用程序,每个月有 1 亿 5 千万人使用。
Gradle
Gradle 正引入 Kotlin 作为编写脚本的语言。
Evernote
Evernote 最近 将 Kotlin 整合到了他们的 Android 客户端。
Uber
Uber 团队使用 Kotlin 来构建内部工具。
Corda
Corda是一个开源分布式分类账号平台,由各大银行提供支持,完全由 Kotlin 构建。
Coursera
Coursera Android 应用程序部分用 Kotlin 编写。
Pivotal
Spring 采用 Kotlin 的语言特性来提供更简洁的 API。
Atlassian
Trello Android应用程序中的所有新代码都用 Kotlin。