使用 Kotlin 構(gòu)建 Android 應(yīng)用 | Kotlin 遷移指南 (上篇)

原創(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)用

image

△ 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

image

在上周舉辦的 Android Dev Summit 2019 大會(huì)上,我們發(fā)布了 Jetpack Compose 的開發(fā)者預(yù)覽版。Jetpack Compose 可以幫助開發(fā)者簡(jiǎn)化并加速 Android 上的 UI 開發(fā)——使用更少的代碼、強(qiáng)大的工具和非常直觀的 Kotlin API,使您的應(yīng)用栩栩如生。

image

我們?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)的文章。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容