使用 Kotlin 構建 Android 應用 | Kotlin 遷移指南 (上篇)

今年五月份的 Google I/O 上,咱們正式向全球宣佈 Kotlin-first 的這一重要理念,Kotlin 將成爲 Android 開發者的首選語言。接下來的幾周咱們將會爲你們連載關於 Kotlin 遷移指南的系列文章,包含 Kotlin 的優點和介紹 (上篇)、遷移到 Kotlin (中篇),以及使用 Kotlin 的常見問題 (下篇),幫助開發者們順利遷移並開始使用 Kotlin 構建 Android 應用。java

瞭解 Kotlin ,以及使用它的優點

Kotlin 是一種現代的靜態設置類型編程語言,能夠提升開發者的工做效率,並提高開發者的工做愉悅度。android

優點 1: 可與 Java 互操做數據庫

與 Android SDK 和 Java 程序語言庫兼容,Kotlin 代碼中能夠方便調用 Java 庫 (Android Studio 的 Lint 檢查亦能與 Kotlin 代碼互操做)。編程

優點 2: 與 IDE 工具兼容canvas

Kotlin 語言由 IntelliJ 的開發團隊設計,可與 IntelliJ (以及 Android Studio) 完美搭配使用,Android Studio 爲 Kotlin 提供了一流的支持,好比,您可經過內置工具來將 Java 代碼轉換成 Kotlin 代碼。或者藉助 「Show Kotlin Bytecode」 工具,您能夠在學習 Kotlin 時查看等效的 Java 代碼。安全

優點 3: 空安全檢測bash

默認狀況下,Kotlin 可避免空指針異常發生。並且能夠在開發時而不是運行時發現和避免錯誤。網絡

fun foo(p: int) { ... }
foo(null) // 編譯器報錯

var o: String? = ...
println(o.toLowerCase()) // 編譯器報錯
複製代碼

△ 上面兩個例子都會觸發編譯器報錯, 從而避免了在運行時出現崩潰異步

優點 4: 更簡潔的代碼編程語言

Kotlin 有着更簡潔明瞭的語法,可減小樣板代碼的使用。

// 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) {...}
}
複製代碼

好比上例中的數據類代碼,有字段以及對應的 getter 和 setter 方法,雖然都是常規內容,但難免繁瑣,並且大量的樣本代碼也會佔用開發者的精力。咱們來看看一樣的類用 Kotlin 如何編寫:

// Kotlin 語言,一樣的類代碼
class User(
    var firstName: String?,
    var lastName: String?
)
複製代碼

Kotlin 還支持擴展方法,能夠給現有的類附加新的方法 (而不須要修改類的原始代碼)。好比咱們想計算字符串內某個字符出現的次數,一般咱們這麼作:

// 定義方法
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
}

// 計算「Elephant」裏有幾個「e」
val string = "Elephant"
howMany(string, 'e')
複製代碼

有了擴展方法,咱們能夠直接把 howMany 這個方法添加至 String 類:

// 擴展方法
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
}

// 執行
val string = "Elephant"
string.howMany('e')
複製代碼

如此一來,咱們就直接 「問」 string 「你裏面有幾個‘e’字符」 就能夠了,這更簡潔、天然,可讀性也大幅提高。

Kotlin 還支持指定/默認參數,這讓開發者在編寫方法時,不須要爲不一樣參數的版本另寫一個方法,而是直接在同一個方法裏,經過 「?」 標出可空參數,經過 「=」 給出參數的默認值便可。

// View.java
public 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) {
    // ...
}

// 和上述內容等效的 Kotlin 代碼
class View(context: Context, attrs: AttributeSet?, defStyleAttr: Int = 0, defStyleRes: Int = 0) {
    // ...
}
複製代碼

△ 使用 Kotlin 僅須要定義一個構造函數便可

優點 5: 語言特性帶來的進階功能

Kotlin 也在持續爲開發者帶來更多高級的語言特性,協程就是一個突出的例子。

Kotlin 裏的協程能夠理解爲從語言級別實現了異步或非阻塞編程,並在 Kotlin 1.3 中開始提供,在 Android 上使用協程能夠避免下面的問題:

  • 經過主 (界面) 線程進行調用時能夠確保安全 (好比在主線程中異步訪問數據庫)
  • 避免在主線程上運行耗時較長的任務 (如圖像或網絡操做) 時發生阻塞

好比下面這個例子,使用協程時不會對主線程形成阻塞,並可提升可讀性:

// 使用回調
fun getData() {
    get("developer.android.google.cn") { result ->
        show(result)
    }
}

// 使用協程
suspend fun getData() {
    val result = get("developer.android.google.cn")
    show(result)
}

suspend fun get(url: String) {...}
複製代碼

使用 Kotlin 構建 Android 應用

△ Kotlin 推動的時間表
使用 Kotlin 更快速地編寫更棒的 Android 應用,自兩年前 Android 平臺開始支持使用 Kotlin 語言後,咱們一直在努力豐富使用 Kotlin 構建的體驗和開發效率的提高。咱們爲 Android 開發者提供了 Android KTX、Android Studio 的支持以及大量的學習資源等。

Android KTX

自從兩年前 Android 平臺開始支持 Kotlin 後,咱們一直在努力解決 Kotlin 的兼容性問題並豐富其功能,更進一步爲你們帶來了許多工具來進一步提升開發效率,好比 Android KTX。它是一組適用於 Android 開發的 Kotlin 擴展功能,對多種經常使用的 Android 開發流程提供簡化的封裝 API。

適用於動畫、圖形、文本等諸多領域。下面來看幾個例子:

KTX: 動畫

AnimatorKt 能讓開發者在動畫的各個階段執行本身的操做。好比之前須要在動畫結束時執行操做須要這麼作:

// Animator API
fun addListener(listener: Animator.AnimatorListener!)

// 應用代碼
val animator = ObjectAnimator.ofFloat(...)
anim.addListener(object : AnimatorListenerAdapter() {
    override fun onAnimationEnd(animation: Animator?) {
        println("end!")
    }
})
複製代碼

而在 AnimatorKt 裏,只需使用 doOnEnd 便可,代碼被精簡成了一行:

// AnimatorKt
inline fun Animator.doOnEnd(
    crossinline action: (animator: Animator) -> Unit
)

// 應用代碼
val animator = ObjectAnimator.ofFloat(...)
anim.doOnEnd { println("end!") }
複製代碼

你們能夠參看以下代碼瞭解 AnimatorKt 是如何幫你們精簡代碼的:

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 轉化爲位圖

將可繪製對象轉化爲位圖是很多開發者在處理 UI 時的經常使用操做,在之前須要如此操做:

// 位圖 API
fun createBitmap(width: Int, height: Int, config: Bitmap.Config): Bitmap

// Canvas API
fun draw(canvas: Canvas)

// 應用代碼
val (oldLeft, oldTop, oldRight, oldBottom) = bounds
drawable.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,只須要以下操做便可,應用代碼再次被壓縮成了一行:

// DrawableKt
fun toBitmap(
    width: Int = intrinsicWidth,
    height: Int = intrinsicHeight,
    config: Config? = null
): Bitmap

// 應用代碼
d.toBitmap(width, height)
複製代碼

DrawableKt 其實是使用擴展方法,將開發者須要作的操做封裝了起來,從而節省了大量重複工做的時間:

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

在推薦開發者使用 Kotlin 構建應用的同時,Android 團隊本身也在大規模的使用 Kotlin,好比下面要跟你們介紹的在 Jetpack 庫中的 Kotlin 特性的使用:

Jetpack 與協程

在 Jetpack 的下述組件庫裏使用了協程的特性:

  • Room: suspend 函數
  • WorkManager: CoroutineWorker
  • Lifecycles: 協程做用域 (coroutine scope)
  • ViewModel: 協程做用域
  • LiveData: 協程構建器 (coroutine builder)

Jetpack Compose

在上週舉辦的 Android Dev Summit 2019 大會上,咱們發佈了 Jetpack Compose 的開發者預覽版。Jetpack Compose 能夠幫助開發者簡化並加速 Android 上的 UI 開發——使用更少的代碼、強大的工具和很是直觀的 Kotlin API,使您的應用栩栩如生。

咱們爲開發者們準備了一些 Jetpack Compose 相關的教程,幫助您更直觀的體驗和了解它的優點: developer.android.google.cn/jetpack/com…

請持續關注咱們接下來時間發佈的與 Kotlin 遷移指南相關的文章。

若是您對在 Android 開發中使用 Kotlin 有任何疑問或者想法,歡迎在評論區和咱們分享。

點擊這裏即刻使用 Kotlin 打造精彩 Android 應用

相關文章
相關標籤/搜索