iT邦幫忙

2021 iThome 鐵人賽

DAY 14
0

Keyword: coroutine


這幾天在使用網路功能時,都使用到了Kotlin的Coroutine,在撰寫KMM乃至於大部分的Kotlin移動端應用,都有Coroutine的身影.

這邊的介紹是簡略版的,主要是提到如何去使用,在KMM的專案中會不避免地遇到許多Coroutine的使用.真的要講的話可以獨立開一個鐵人賽項目.

剛好我們團員這次有針對Coroutine進行鐵人賽!內容非常詳盡!如果想要暸解更多,歡迎訂閱

Coroutine 停看聽 :: 2021 iThome 鐵人賽

Coroutine?

Coroutine是一個自創詞,由代表共同的Co與工作常規的routine兩個詞合併起來,在wiki上面被翻作"共常",但是我更喜歡大陸普遍的說法,“協程”.描述了Coroutine讓不同工作項共同運作的特色.

在Kotlin的Coroutine中,當某個函式正在等待某個事件發生的時候,可能是等待網路請求的結果,可能是等待使用者的回應或操作,甚至可能是印表機印速度太慢,導致Cpu無事可幹.白白浪費掉這些效能,所以就有人想到,我們能不能讓等待事件結果的函式,先把目前的狀態儲存起來,解放這些寶貴的資源,先給現在正需要資源的其他人,等到等待的事件發生了,再從暫存狀態復原回來,繼續執行.這就是Coroutine環境的特色

Kotlin的Coroutine,和一般常說Coroutine上有些許差別,由於Kotlin是跑在JVM上的,而嚴格來說,JVM沒有提供真正的Coroutine環境,因此實際上還是用Thread去模擬Coroutine環境.在與其他語言的使用者交流時可能需要注意到這點.
另外,Kotlin正在開發其他語言的版本,如果在其他語言平台,且有支援Coroutine環境,那時就能真正使用到Coroutine,而不是JVM上以Thread模擬的Coroutine環境.

實際範例

interface CafeApi {
    suspend fun fetchCafeFromApi(city:String): List<CafeResponseItem>
}

這是前幾天寫在shared內的一個suspend 函式,suspend函式就有如上面介紹的一樣,具有暫時交出控制權並且暫存目前狀況的能力,因為這是一個Ktor的網路請求,所以需要等待發生的事件,就在發出網路請求後,一直到網路請求的回應.如果使用普通的Thread,在這段時間內Thread都是浪費的.所以把這個function標註為suspend,讓Kotlin減少浪費的資源與效能.

我們在Android內部使用Coroutine的時候,就會像

class MainViewModel : ViewModel() {
    ...
    fun fetchCafeData(city: String = "") {
        viewModelScope.launch() {
            val result = async { dataRepository.fetchCafesFromNetwork(city) }
            cafeList.value = result.await()
        }
    }
  	...
}

我們外面使用了一個scope包裹了所有的suspend函式,由於suspend函式可以回復的特性,必須運行在一個coroutineScope中,不然需要回復狀態時,suspend函式不知道自己該回到哪邊.

而scope有許多種,畫面層使用的lifecycleScope,ViewModel曾使用的viewModelScope,以及全局共用的globalScope等等.這次就是用跟ViewModel生命週期綁定的viewModelScope

由於這次是拉取網路資料,因此這個fetchCafesFromNetwork將會有回傳值,我們使用async關鍵字將這個部分包裹起來,並且將回傳的部分用一個result參數代表.

當我們要使用到其中的數據時,使用await()呼叫,這樣在結果回來前,這段的coroutine都會被suspend,直到有結果回傳.

寫一塊來驗證看看

class MainViewModel : ViewModel() {
    ...
   fun checkAsync() {
        viewModelScope.launch() {
            val result1 = async { delaySuspendFunction1() }
            val result2 = async { delaySuspendFunction2() }
            println("job1 : "+result1.await()+ " at" + System.currentTimeMillis())
            println("job2 : "+result2.await()+ " at" + System.currentTimeMillis())

        }
    }
    private suspend fun delaySuspendFunction1() {
        withContext(Dispatchers.IO){
            delay(1000L)
            "result1 "
        }
    }
    private suspend fun delaySuspendFunction2() {
        withContext(Dispatchers.IO){
            delay(10L)
            "result2 "
        }
    }
  	...
}

可以看到在job1印出來後,幾乎馬上job2的文字就出現了,因為job2早就執行完了,在等待job1的await回傳,可以把兩行的順序交換,看看有什麼結果.

今天先大概介紹到這邊,明天會來講Coroutine 的一些注意點


上一篇
Day 13:因應在地口味調整,根據各平台實作功能!
下一篇
Day 15:完了,我的Coroutine漏出去了.Coroutine的Leak與結構化
系列文
挑戰 Kotlin Multiplatform Mobile 跨平台開發,透過共同的Kotlin模組同時打造iOS與Android應用!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言