原創(chuàng): Android 谷歌開發(fā)者
今年五月份的 Google I/O 上,我們正式向全球宣布 Kotlin-first 的這一重要理念,Kotlin 將成為 Android 開發(fā)者的首選語言。接下來的幾周我們將會(huì)為大家連載關(guān)于 Kotlin 遷移指南的系列文章,包含 Kotlin 的優(yōu)勢(shì)和介紹 (上篇)、遷移到 Kotlin (中篇),以及使用 Kotlin 的常見問題 (下篇),幫助開發(fā)者們順利遷移并開始使用 Kotlin 構(gòu)建 Android 應(yīng)用。
了解 Kotlin ,以及使用它的優(yōu)勢(shì)
Kotlin 是一種現(xiàn)代的靜態(tài)設(shè)置類型編程語言,可以提高開發(fā)者的工作效率,并提升開發(fā)者的工作愉悅度。
優(yōu)勢(shì) 1: 可與 Java 互操作
與 Android SDK 和 Java 程序語言庫兼容,Kotlin 代碼中可以方便調(diào)用 Java 庫 (Android Studio 的 Lint 檢查亦能與 Kotlin 代碼互操作)。
Kotlin 互操作指南
https://developer.android.google.cn/kotlin/interop
優(yōu)勢(shì) 2: 與 IDE 工具兼容
Kotlin 語言由 IntelliJ 的開發(fā)團(tuán)隊(duì)設(shè)計(jì),可與 IntelliJ (以及 Android Studio) 完美搭配使用,Android Studio 為 Kotlin 提供了一流的支持,比如,您可通過內(nèi)置工具來將 Java 代碼轉(zhuǎn)換成 Kotlin 代碼。或者借助 “Show Kotlin Bytecode” 工具,您可以在學(xué)習(xí) Kotlin 時(shí)查看等效的 Java 代碼。
優(yōu)勢(shì) 3: 空安全檢測(cè)
默認(rèn)情況下,Kotlin 可避免空指針異常發(fā)生。而且可以在開發(fā)時(shí)而不是運(yùn)行時(shí)發(fā)現(xiàn)和避免錯(cuò)誤。
fun foo(p: int) {
...
}
foo(null) // 編譯器報(bào)錯(cuò)
var o: String? = ...println(o.toLowerCase()) // 編譯器報(bào)錯(cuò)
△ 上面兩個(gè)例子都會(huì)觸發(fā)編譯器報(bào)錯(cuò),
從而避免了在運(yùn)行時(shí)出現(xiàn)崩潰
優(yōu)勢(shì) 4: 更簡(jiǎn)潔的代碼
Kotlin 有著更簡(jiǎn)潔明了的語法,可減少樣板代碼的使用。
// Java 語言類代碼
public class User {
private String firstName;
private String lastName;
public User(String firstName, String lastName) {
...
}
public String getFirstName() {
...
}
public void setFirstName(String firstName) {
...
}
public String getLastName() {
...
}
public void setLastName(String lastName) {
...
}
}
比如上例中的數(shù)據(jù)類代碼,有字段以及對(duì)應(yīng)的 getter 和 setter 方法,雖然都是常規(guī)內(nèi)容,但不免繁瑣,而且大量的樣本代碼也會(huì)占用開發(fā)者的精力。我們來看看同樣的類用 Kotlin 如何編寫:
// Kotlin 語言,同樣的類代碼
class User(var firstName: String?, var lastName: String?)
Kotlin 還支持?jǐn)U展方法,可以給現(xiàn)有的類附加新的方法 (而不需要修改類的原始代碼)。比如我們想計(jì)算字符串內(nèi)某個(gè)字符出現(xiàn)的次數(shù),通常我們這么做:
// 定義方法
fun howMany(string: String, char: Char): Int {
var count = 0
val lowerCaseLetter = char.toLowerCase()
for (i in 0 until string.length) {
if (lowerCaseLetter == string[i].toLowerCase()) count++
}
return count
}
// 計(jì)算“Elephant”里有幾個(gè)“e”
val string = "Elephant"howMany(string, 'e')
有了擴(kuò)展方法,我們可以直接把 howMany 這個(gè)方法添加至 String 類:
// 擴(kuò)展方法
fun String.howMany(char: Char): Int {
var count = 0
val lowerCaseLetter = char.toLowerCase()
for (i in 0 until length) {
if (lowerCaseLetter == this[i].toLowerCase()) count++
}
return count
}
// 執(zhí)行
val string = "Elephant"string.howMany('e')
如此一來,我們就直接 “問” string “你里面有幾個(gè)‘e’字符” 就可以了,這更簡(jiǎn)潔、自然,可讀性也大幅提升。
Kotlin 還支持指定/默認(rèn)參數(shù),這讓開發(fā)者在編寫方法時(shí),不需要為不同參數(shù)的版本另寫一個(gè)方法,而是直接在同一個(gè)方法里,通過 “?” 標(biāo)出可空參數(shù),通過 “=” 給出參數(shù)的默認(rèn)值即可。
// View.javapublic
View(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
// ...
}
// 和上述內(nèi)容等效的 Kotlin 代碼
class View(context: Context, attrs: AttributeSet?, defStyleAttr: Int = 0, defStyleRes: Int = 0) {
// ...
}
△ 使用 Kotlin 僅需要定義一個(gè)構(gòu)造函數(shù)即可
優(yōu)勢(shì) 5: 語言特性帶來的進(jìn)階功能
Kotlin 也在持續(xù)為開發(fā)者帶來更多高級(jí)的語言特性,協(xié)程就是一個(gè)突出的例子。
Kotlin 里的協(xié)程可以理解為從語言級(jí)別實(shí)現(xiàn)了異步或非阻塞編程,并在 Kotlin 1.3 中開始提供,在 Android 上使用協(xié)程可以避免下面的問題:
通過主 (界面) 線程進(jìn)行調(diào)用時(shí)可以確保安全 (比如在主線程中異步訪問數(shù)據(jù)庫)
避免在主線程上運(yùn)行耗時(shí)較長(zhǎng)的任務(wù) (如圖像或網(wǎng)絡(luò)操作) 時(shí)發(fā)生阻塞
比如下面這個(gè)例子,使用協(xié)程時(shí)不會(huì)對(duì)主線程造成阻塞,并可提高可讀性:
// 使用回調(diào)
fun getData() {
get("developer.android.google.cn") {result ->
show(result)
}
}
// 使用協(xié)程
suspend fun getData() {
val result = get("developer.android.google.cn")
show(result)
}
suspend fun get(url: String) {
...
}
使用 Kotlin 構(gòu)建 Android 應(yīng)用
△ Kotlin 推進(jìn)的時(shí)間表
使用 Kotlin 更快速地編寫更棒的 Android 應(yīng)用,自兩年前 Android 平臺(tái)開始支持使用 Kotlin 語言后,我們一直在努力豐富使用 Kotlin 構(gòu)建的體驗(yàn)和開發(fā)效率的提升。我們?yōu)?Android 開發(fā)者提供了 Android KTX、Android Studio 的支持以及大量的學(xué)習(xí)資源等。
Android KTX
自從兩年前 Android 平臺(tái)開始支持 Kotlin 后,我們一直在努力解決 Kotlin 的兼容性問題并豐富其功能,更進(jìn)一步為大家?guī)砹嗽S多工具來進(jìn)一步提高開發(fā)效率,比如 Android KTX。它是一組適用于 Android 開發(fā)的 Kotlin 擴(kuò)展功能,對(duì)多種常用的 Android 開發(fā)流程提供簡(jiǎn)化的封裝 API。
適用于動(dòng)畫、圖形、文本等諸多領(lǐng)域。下面來看幾個(gè)例子:
KTX: 動(dòng)畫
AnimatorKt 能讓開發(fā)者在動(dòng)畫的各個(gè)階段執(zhí)行自己的操作。比如以前需要在動(dòng)畫結(jié)束時(shí)執(zhí)行操作需要這么做:
// Animator API
fun addListener(listener: Animator.AnimatorListener!)
// 應(yīng)用代碼
val animator = ObjectAnimator.ofFloat(...)
animator.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
println("end!")
}
})
而在 AnimatorKt 里,只需使用 doOnEnd 即可,代碼被精簡(jiǎn)成了一行:
// AnimatorKt
inline fun Animator.doOnEnd( crossinline action: (animator: Animator) -> Unit)
// 應(yīng)用代碼
val animator = ObjectAnimator.ofFloat(...)
animator.doOnEnd { println("end!") }
大家可以參看如下代碼了解 AnimatorKt 是如何幫大家精簡(jiǎn)代碼的:
inline fun Animator.doOnEnd(crossinline action: (animator: Animator) -> Unit) =
addListener(onEnd = action)
inline fun Animator.addListener(
crossinline onEnd: (animator: Animator) -> Unit = {},
crossinline onStart: (animator: Animator) -> Unit = {},
crossinline onCancel: (animator: Animator) -> Unit = {},
crossinline onRepeat: (animator: Animator) -> Unit = {}
): Animator.AnimatorListener {
val listener = object : Animator.AnimatorListener {
override fun onAnimationRepeat(animator: Animator) = onRepeat(animator)
override fun onAnimationEnd(animator: Animator) = onEnd(animator)
override fun onAnimationCancel(animator: Animator) = onCancel(animator)
override fun onAnimationStart(animator: Animator) = onStart(animator)
}
addListener(listener)
return listener
}
KTX: Drawables 轉(zhuǎn)化為位圖
將可繪制對(duì)象轉(zhuǎn)化為位圖是不少開發(fā)者在處理 UI 時(shí)的常用操作,在以前需要如此操作:
// 位圖 API
fun createBitmap(width: Int, height: Int, config: Bitmap.Config): Bitmap
// Canvas API
fun draw(canvas: Canvas)
// 應(yīng)用代碼
val (oldLeft, oldTop, oldRight, oldBottom) = boundsdrawable.setBounds(0, 0, width, height)
val bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888)drawable.draw(Canvas(bitmap))drawable.setBounds(oldLeft, oldTop, oldRight, oldBottom)
但如果使用 DrawableKt,只需要如下操作即可,應(yīng)用代碼再次被壓縮成了一行:
// DrawableKt
fun toBitmap(
width: Int = intrinsicWidth,
height: Int = intrinsicHeight, config: Config? = null): Bitmap
// 應(yīng)用代碼
d.toBitmap(width, height)
DrawableKt 實(shí)際上是使用擴(kuò)展方法,將開發(fā)者需要做的操作封裝了起來,從而節(jié)省了大量重復(fù)工作的時(shí)間:
fun Drawable.toBitmap(
@Px width: Int = intrinsicWidth,
@Px height: Int = intrinsicHeight,
config: Config? = null): Bitmap {
if (this is BitmapDrawable) {
if (config == null || bitmap.config == config) {
if (width == intrinsicWidth && height == intrinsicHeight) {
return bitmap
}
return Bitmap.createScaledBitmap(bitmap, width, height, true)
}
}
val (oldLeft, oldTop, oldRight, oldBottom) = bounds
val bitmap = Bitmap.createBitmap(width, height, config ?: Config.ARGB_8888)
setBounds(0, 0, width, height)
draw(Canvas(bitmap))
setBounds(oldLeft, oldTop, oldRight, oldBottom)
return bitmap
}
Kotlin x Jetpack
在推薦開發(fā)者使用 Kotlin 構(gòu)建應(yīng)用的同時(shí),Android 團(tuán)隊(duì)自己也在大規(guī)模的使用 Kotlin,比如下面要跟大家介紹的在 Jetpack 庫中的 Kotlin 特性的使用:
Jetpack 與協(xié)程
在 Jetpack 的下述組件庫里使用了協(xié)程的特性:
Room: suspend 函數(shù)
WorkManager: CoroutineWorker
Lifecycles: 協(xié)程作用域 (coroutine scope)
ViewModel: 協(xié)程作用域
LiveData: 協(xié)程構(gòu)建器 (coroutine builder)
Jetpack Compose
在上周舉辦的 Android Dev Summit 2019 大會(huì)上,我們發(fā)布了 Jetpack Compose 的開發(fā)者預(yù)覽版。Jetpack Compose 可以幫助開發(fā)者簡(jiǎn)化并加速 Android 上的 UI 開發(fā)——使用更少的代碼、強(qiáng)大的工具和非常直觀的 Kotlin API,使您的應(yīng)用栩栩如生。
我們?yōu)殚_發(fā)者們準(zhǔn)備了一些 Jetpack Compose 相關(guān)的教程,幫助您更直觀的體驗(yàn)和了解它的優(yōu)勢(shì): https://developer.android.google.cn/jetpack/compose/tutorial
請(qǐng)持續(xù)關(guān)注我們接下來時(shí)間發(fā)布的與 Kotlin 遷移指南相關(guān)的文章。