美文网首页Kotlin
Android 开发进阶:Kotlin Coroutines 使

Android 开发进阶:Kotlin Coroutines 使

作者: you的日常 | 来源:发表于2022-05-20 18:23 被阅读0次

还记得第一次听到 Coroutines 的时候,纳闷了一下,口瑞停,这是什么新的番号招式(误),之后其实也没有多在意了,好一段时间,因为一个档案的 I/O 会把 UI Thread 卡住,必须要用异步程序去处理,写 Handler Thread 可以避免,这也是最基础的方式,缺点也很明显某些时候还是避不掉,写 RX 又总觉得微妙感觉有点杀鸡用牛刀的感觉,后来看了一下决定用 Coroutines,于是有了本篇文章。

image.png

是什么问题要解决?

functionA()
functionB()
functionC()

普通情况下,执行的顺序会是很直白的 functionA() -> functionB() -> functionC()

如果只有一个 thread ,这样很顺畅没问题。

但假如这是一个跑在 main thread 上,而 ·function A· 是需要另一个 thread 的处理结果,而该结果是需要该 thread 耗费长时间作业才可以获得的。这边姑且称为 IO thread 好了。那不就意味着 function A 得等到 IO thread 处理结束并告知结果才能继续执行 function A 乃至 function B 之后才是 function C 呢?

那在等待 function A 的时候 main thread 啥事都不能做,只能 idle 在那边动也不动。

这如果是在 Android main thread 上,这样的行为会让画面 freeze ,时间稍微长一点就会 ANR 被 OS 当作坏掉进行异常排除了。

其实这个异步问题解决方案很多,诸如自己写一个 callback ,或者自干 Handler thread 去控管或者是用 RX ,去订阅之类。某些时候显得不直观,或者使用起来麻烦,总有种杀鸡何需使用牛刀的感觉。

那有没有可能?我就写成上面那样,但是当 function A 在等待 IO thread 时,让 main thread 去做其他的事情,等到 IO thread 结束耗时处理后,再回来继续执行 function Afunction Bfunction C 呢?

是的,可以,这个解决方案便是 Coroutine

Coroutines 到底是什么?

Coroutines ,这个单字会被标成错字,理由是他其实是两个单字合并而成的,分别是 cooperation + routine, cooperation 意指合作,routine 意指例行作业、惯例,照这里直接翻译就会是合作式例行作业。

想到辉夜姬让人想告白提到了惯例行为,也是念作 routine

那我们看到的翻译多半会是协程、协作程序…这样讲没啥前后感,谁协助程序?协助啥程序? 总之就是满头的问号。

这里 routine 指得是程序中被呼叫的 function、method ,也就是说,我们将 function 、method 协同其他更多的 function、method 共同作业这件事情称为 Coroutines

协同作业听起来还是很抽象,具体协同什么呢?

这便是 Coroutines 最典型的特色,允许 method 被暂停( suspended)执行之后再回复(resumed)执行,而暂停执行的 method 状态允许被保留,复原后再以暂停时的状态继续执行。

image.png

换句话说,就是我在 main thread 执行到 function A 需要等 IO thread 耗时处理的结果,那我先暂停 function A, 协调让出 main thread 让 main thread 去执行其他的事情,等到 IO thread 的耗时处理结束后得到结果后再回复 function A 继续执行,已得到我要的结果,这便是 Coroutines 的概念,这听起来不是很简单了呢?

事实上这个概念早在 1964 年就已经被提出了,而很多语言也都有这样的概念,只是 Android 上头类似的东西一直没有被积极推广,直到 Kotlin 成为官方语言后,Coroutines 以 Support Library 的形式被推广才又在 Android 业界流行起来。

Kotlin Coroutines

首先,因为 Kotlin 的 Coroutine 并没有包含在原有包装中,而是以 Support Library 的形式提供开发者使用,所以需要另外导入该 Library。

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9'

这里选用这个版本进行演示,实际中可以根据自己的需要修改版本。

那因为是在 Android 上使用的, Android 上头的 main thread 跟普通 java 有点不一样,所以还需要另一个 implementation,不然会报错。

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'

导入之后就可以开始使用了。

一个简单的开始

这边我想做的是画面上有一个会倒数的 Text ,用 Coroutines 可以简单地做到

class CoroutineActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_coroutine)
        GlobalScope.launch(Dispatchers.Main) {

            for (i in 10 downTo 1) { 
                textView.text = "count down $i ..." // update text
                delay(1000) 
            }
            textView.text = "Done!"
        }
    }

}

那跑起来结果就像这样:


image.png

这样如果要 Thread 有相同的结果可以写成这样:

Thread {
    for (i in 10 downTo 1) {
        Thread.sleep(1000)
        runOnUiThread {
            textView.text = "count down $i ..." 
        }
    }
    runOnUiThread {
        textView.text = "Done!"
    }


}.start()

这样会有什么问题就是另一个故事了,至少现在这样不会马上出现 Exception (最常见的就是使用者离开画面没多久就出现一个 Exception),不过也并不是说用 Coroutines 就不会发生这些问题,记得这些做法没有什么优劣,差别在都选择就是了。

说回 Coroutines ,那跟 Thread 一样,某些时候我们会想要临时把它停住,那 GlobalScope.launch 会回传一个 Job class 的玩意

val job: Job = GlobalScope.launch(Dispatchers.Main) {
    // launch coroutine in the main thread
    for (i in 10 downTo 1) { // countdown from 10 to 1
        textView.text = "count down $i ..." // update text
        delay(1000) // wait half a second
    }
    textView.text = "Done!"
}

想要把它停住的话就用 cancel 即可

job.cancel()
Scope
GlobalScope 是什么玩意?

Scope 指得是范围,Coroutines 可以作用的范围。在 Main thread 上或是 IO thread 上,又或者希望开更多的 Worker thread,然后是可以在某个控制流(e.g Activity 的生命周期)中可被控制的。

原则上,在 Kotlin 里头使用任何标记 suspend 的 method(后面会提)都会在 Scope 里面,这样才可以控制 Coroutines 的行进与存活与否。

那这边举的例子, GlobalScope 继承自 CoroutineScope。它是 CoroutineScope 的一个实作,它的概念就是最高层级的 Coroutines ,它的作用的范围伴随着 Application 的生命周期,那其实他的概念与 Dispatch.Unconfined 相同(待会会提到),用他其实可以避免 Coroutines 被过早结束,但也要记得是,这个用法类似直接呼叫静态函数,需要注意。

那如果直接实作 CoroutineScope 呢?

class CoroutineActivity : AppCompatActivity(), CoroutineScope {
    override val coroutineContext: CoroutineContext
        get() = TODO("not implemented")
//...ignore
}

那会要求实作一个 CoroutineContext ,这是什么玩意?指的就是 Coroutines 作用的情景,这边可以指定他是在 Main thread 上或者就直接弄一个 Job 给他:

class CoroutineActivity : AppCompatActivity(), CoroutineScope {
    override val coroutineContext: CoroutineContext
        get() = job

    private val job = Job()
//...ignore
}

这样 launch 的时候就会使用这个 Job 来操作了,如果没有特别定义,那这个 Job 就是跑在 Worker thread 上了,用它更新 UI 会出现 Exception ,这方面可以依据自己的需求去做调整。

不过更多时候我会希望他能够跑在 Main Thread 上,Koltinx Coroutine 有提供 CoroutineScope 的实作 - MainScrope

相关文章

网友评论

    本文标题:Android 开发进阶:Kotlin Coroutines 使

    本文链接:https://www.haomeiwen.com/subject/azrnurtx.html