iT邦幫忙

2022 iThome 鐵人賽

DAY 14
0
Mobile Development

Kotlin 全面啟動 系列 第 14

[Kotlin 全面啟動] Coroutine III

  • 分享至 

  • xImage
  •  

本文就是 Coroutine 的最後一篇了,當然圍繞著 Coroutine 一定還有很多主題可以分享,但局限於鐵人賽篇幅,今天我們就著重在於怎麼切 Thread 以及 Android / iOS 上的運用。

CoroutineContext

如果學習過 Android 開發一定對 Context 這個詞很熟悉,但 Context 同時也非常難用中文表示,直翻叫做上下文,代表著一個讓我們可以得到某些能力的環境參數,而 CoroutineContext 就是在 Coroutine 裡使用的 Context,這可以拿來做什麼呢?其中一個就是指定 Thread。

Dispatchers

Dispatchers 是 Coroutine 指定 Thread 執行的一個常數,而它也是一種 CoroutineContext,常用有以下幾種:

  • Dispatchers.Default : Default 的 Dispatcher,通常跟 CPU 的個數相同
  • Dispatchers.Main : 將會執行在 Main thread 上
  • Dispatchers.IO : 將會執行在 IO thread 上

而不管是之前介紹的 launch 或是 runBlocking 其實都可以再多加上一個 CoroutineContext 讓我們修改當前的 Context,所以如果想要讓 lambda block 裡的程式執行在指定的 Dispatcher 下只要類似如下呼叫就可以了:

launch(Dispatchers.IO) {
    println("World!") // this will run in IO thread
}

而如果不想建立新的 Coroutine,只是想切換執行的 Thread,也可以使用 withContext 來切換,範例如下:

withContext(Dispatchers.IO) {
    println("World!") // this will run in IO thread
}

值得一提的是 withContext 會把最後一行的執行結果回傳,所以當我們把 function 都使用 withContext 指定好執行的 Thread 的時候,就可以平鋪直敘的寫非同步代碼了,範例如下:

suspend fun apiCall(): String = withContext(Dispatchers.IO) {
    // call api
    return@withContext ""
}

suspend fun displayUI(data: String) = withContext(Dispatchers.Main) {
    // display UI
}

val result = apiCall() // IO Thread
displayUI(result) // Main Thread

Android

由於 Android 也可以使用 Kotlin 直接寫,所以使用上其實跟我們之前所介紹的沒什麼二樣,只是 Google 有提供 Android 使用的 CoroutineScopeviewModelScope 以及 lifecycleScope,分別可以在 ViewModel 以及 UI 層使用,就可以更專注在商業邏輯上而不用煩惱建立變數以及管理何時需要呼叫 cancel 了。

iOS

而 iOS 的使用相對來說就是個大問題,首先 Swift 裡面根本就不會有任何 Kotlin 的 CoroutineScope 可以使用,而且 suspend 在 Swift 裡也不會有特殊功能,那 KMM 要怎麼處理這個問題呢?

我們可以試試看在 shared module 裡建立一個 public 的 suspend function,然後從 iOS 這邊看這個 function 的定義,就會發現 suspend function 在 iOS 會自動轉變成 callback 的形式,多了一個名叫 completionHandler 的 callback,來通知結果或是異常,範例如下:

// shared
suspend fun getUser(userName: String): User

// iOSMain
getUser(userName: "Jintin", completionHandler: { user, error in
    ...
})

但實際上跑起來會出現以下的問題:

Trying to access top level value not marked as @ThreadLocal or @SharedImmutable from non-main thread

這是因為之前提到的 Kotlin Native 在 multi-Thread 的環境需要些額外的設定,但這些在新的 memory manager 之中是不需要特別處理的,可以在 gradle.properties 加上以下的 config 就可以正常了:

kotlin.native.binary.memoryModel=experimental

如果是持續關注本系列文章的讀者,應該會有印象我們在第四天其實就有提到這個 Kotlin/Native 的 memory manager 吧,讓我們一起期待 KMM 能盡快順利的升上 Beta 以及 Stable 囉~


上一篇
[Kotlin 全面啟動] Coroutine II
下一篇
[Kotlin 全面啟動] Flow
系列文
Kotlin 全面啟動 30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言