iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 4
0
自我挑戰組

Android Architecture 及 Unit Test系列 第 4

[Day 4] Kotlin Coroutines:Part 2 Scope、Suspend & Dispatcher

  • 分享至 

  • xImage
  •  

昨天建好了 coroutines 的環境,今天來說說 coroutines 的幾個重要的特性。

本質上,Coroutines 是輕量級的線程,他們在 CoroutineScope 裡與 Coroutines 調度器 (launcher) 一起被調用,這裡我會一一介紹這些關鍵詞。

Scope

CoroutineScope 指的是協程作用的範圍,可以是作用在 Main thread 或是 IO thread 上,依情況而定。

CoroutineScope 內有一個 CoroutineContextCoroutineContext 指的是 coroutines 作用的情境,也就是 Main thread 或是 IO thread 等。

另外在寫 Coroutines 時常見的 GlobalScope 也是繼承 ConroutineScope ,指的就是作用在整個 Application 上,伴隨 APP 的生命週期。

當然我們也可以自己實作一個 CoroutineScope

class MainActivity : AppCompatActivity(), CoroutineScope {
    
    override val coroutineContext: CoroutineContext
        get() = job
        
    val job = Job() // 默認使用預設 thread

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

可以看到 CoroutineScope 要求實作一個 CoroutineContext ,我們在這邊給他一個 Job

Job 指的是用來控制 Coroutines 的類,常見的有以下幾種控制方式:

  • cancel():取消 Coroutines
  • join():等待 Job 執行結束
  • cancelAndJoin():其實就是上面兩者的合併方法
  • isActive:確認 Coroutines 是否執行中

Job 有點類似 Java 用來控制線程的 Thread 。也可以透過 Job 來決定 Coroutines 要跑在哪個 thread 上,默認使用 Worker Thread

在 Android 裡 Scope 還有一個常見的實作類 MainScope ,指的就是在 Main thread 執行的 Scope,需要注意的是如果要配合 Activity 的生命週期,記得要在 onDestroy 時取消 MainScope

另外 AndroidX LifeCycle 現在也實作了一個對應 ViewModel 生命週期的 viewModelScope ,同樣也是執行在 Main Thread 上。

Suspend

昨天在範例程式中所宣告的 function 前面都有加了一個前綴 suspend ,那麼 suspend 又是什麼意思呢?

suspend 在英文中指的是 "掛起" 的意思,可以理解為暫停,表示這個 function 是可以被 "暫停" 的。suspend function 可以被其他的 suspend function 調用,但是不能被一般的 function 調用,因此 coroutines 提供了幾個 coroutine builders :

  • runblock:啟動一個協程並 block 此線程直到協程完成

  • launch:在不阻塞當前線程的情況下啟動一個協程並返回一個 Job

  • withContext:不會建立新的協程,而是在指定的協程上運行

  • async:在不阻塞當前線程的情況下啟動一個協程並返回一個叫 Defered 的 class,需要與 await 一起使用

Defered 有點類似 Future ,它使一個協程可以 suspend 並等待另一個協程產生結果,直到另一個協程完成。

事實上 asyncawait 這樣的使用模式比較接近其他語言的 Coroutine 使用方式,但是通常而言在 Kotlin 實現 coroutines 比較常使用前三者,舉個例子:

MainScope.launch {
    val result = withContext(Dispatchers.IO) { blockForIO() }
    // ... 直接在這裡使用 result ,此時程式在 UI Thread。
}

這樣的寫法等同於 "等待 result 產生結果,再使用 result"。

Dispatchers

直接翻譯過來就是調度員。其實就是 Kotlin Coroutines 預先準備好供開發者使用的 CoroutineContext ,統一繼承自 CoroutineDispatcher 並實現以下幾種:

  • Dispatchers.Default:使用的 Thread 取決於調用時當下的 Thread,最大並行的協程數量則依據系統的 cpu 核心數量,最少為兩個,概念相當於 RxJava 的 Schedulers.computation()
  • Dispatchers.Main:即跑在 Main Thread 上的包裝
  • Dispatchers.IO:會開一條 Worked Thread,並在上面執行,適合用在 IO 之類的耗時操作
  • Dispatchers.Unconfined:會跑在開啟時當下的 Thread ,但是一旦在 suspend 後再被恢復,可能會執行在其他線程上

以 Android 的 MainScope 為例,其實就是提供 Dispatchers.Main 當作 CoroutineContext 的 Scope。

明天再說說 Coroutines 的一些用法。


上一篇
[Day 3] Kotlin Coroutines:Part 1 Basic
下一篇
[Day 5] Kotlin Coroutines:Part 3 Real Work
系列文
Android Architecture 及 Unit Test30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言