Day 14:Coroutine,那是什麽?好吃嘛?

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 的一些注意点


<<:  自动化初步-试着用pyautogui操作一般软件

>>:  D4 - 彭彭的课程#Python 简介、安装、与快速开始

【第二十天 - PHP反序列化(2)】

Q4. PHP 反序列化利用? POP Chain 常见的涵数:exec()、passthru()、...

Day-2 Excel出现#字号!难道是中毒了吗!?

你是不是常常在编辑试算表时遇到”####”呢?别紧张,其实出现这个符号并不是你输入错误喔,而是你的栏...

[Day 06] tinyML的重要推手Arm Cortex-M MCU

在[Day 01]时就有提及,未来具有边缘智能(Edge AI)的智慧物联网(AIoT)装置一定少不...

Day6 宣告元件 - Class Component

Class Component用的是ES6中的class语法建立元件,接着去 extends(继承)...

Rails belong_to

belongs_to 的设定 optional 在 Rails 5.1 之後的版本,belongs_...