一学就会的协程使用——基础篇(五)再遇取消

1. 引言

前面已经知道了协程作用域和协程取消的真正作用了,现在结合着协程作用域和withContext来再次体会下协程取消的便捷。

2. 实践代码说明

本文关键代码(按钮的点击事件):

viewBinding.launchBtn -> {
    "Clicked launchBtn".let {
        myLog(it)
    }
    scope.launch(Dispatchers.IO) {
        "Coroutine IO runs (from launchBtn)".let {
            myLog(it)
        }
        Thread.sleep(FIVE_SECONDS)
        "Coroutine IO runs after thread sleep".let {
            myLog(it)
        }
        withContext(Dispatchers.Main) {
            "withContext(Dispatchers.Main) lambda".let {
                myLog(it)
            }
        }
    }
}

关键的代码逻辑很简单——

启动一个在IO线程的协程,协程输出第一行log——"Coroutine IO runs (from launchBtn)";

然后休眠线程5秒,输出第二行log——"Coroutine IO runs after thread sleep";

最后切换到主线程,输出第三行log——"withContext(Dispatchers.Main) lambda"。

这里所用的协程作用域跟前篇一样,是Activity中的属性:

private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)

生命周期onDestroy中:

override fun onDestroy() {
    super.onDestroy()
    scope.cancel()
}

另外一个按钮,点击时取消已经启动的协程:

viewBinding.cancelBtn -> {
    "Clicked cancelBtn".let {
        myLog(it)
    }
    scope.coroutineContext.cancelChildren()
}

3. 实践过程说明

在启动协程后,5秒以内点击取消按钮或者退出当前页面,可以发现,协程的前两行log会始终输出,但是在第三行log却不会输出,不点击取消按钮和始终停留在当前页面的话,第三行log会正常输出。

为什么取消后第二行log始终输出,而第三行log不输出了呢??

一学就会的协程使用——基础篇(三)初遇协程取消中,提及过协程的取消是需要协作的,也就是说,协程的取消需要在执行逻辑中需要有协作点!初遇篇所用的协程取消点主要是isActiveensureActive(),这里初看并没有协程取消协作点,为什么第三行log不支持了呢?

这里便是本文的重点,所有kotlinx.coroutines包下的挂起函数都是可被取消的:

所有 kotlinx.coroutines 中的挂起函数都是 可被取消的 。它们检查协程的取消, 并在取消时抛出 CancellationException。

上述描述出自中文文档:https://www.kotlincn.net/docs/reference/coroutines/cancellation-and-timeouts.html

withContext是kotlinx.coroutines包下的挂起函数,如上描述,是可被取消的。所以协程在执行到withContext一行时,触发到协作点时,如果协程已经被取消,所以协作点生效。

这里便是解释了第三行log在取消后不再输出的问题。进一步地,为什么第二行log的执行时机也在协程取消以后,但第二行log始终会输出呢?这里必须再强调:

协程的取消是 协作 的。一段协程代码必须协作才能被取消。

也就是说,协程代码执行过程中,如果没有取消协作点,即使在协程执行到具体代码位置时协程已经被取消,协程仍会继续执行!

在第二行代码执行之时,没有任何协程取消协作点,所以不管执行第二行log输出之时协程是否已经被取消,第二行log始终会输出。

第三行log不输出,是因为挂起函数withContext是可取消的,也就是在withContext挂起函数执行的时候,才触发了协程取消的协作点,进而使得协程取消!

切记,协程取消不是万能钥匙,调用了协程的取消后,协程并不能在任意位置停止执行,只有执行到协作点的时候,协程的取消才会生效!

其实,想要在5秒内点击取消后第二行log也不输出,也很简单,在第二行log输出前,增加协程取消的协作点,即调用ensureActive()函数即可!

4. 关于挂起函数的提醒

文档中描述,kotlinx.coroutines 中的挂起函数都是可被取消的。注意限定词,可被取消的不是挂起函数,是kotlinx.coroutines 中的挂起函数。

也就是说,挂起函数本身是不提供取消功能,只不过是kotlinx.coroutines 中的挂起函数中实现了对协程取消的协作代码。这里主要强调第一个容易误解的点:

挂起函数本身不会提供协程取消协作点,而是协程特定包下中的挂起函数内部实现代码提供了取消协作点。

事实上,上面说的可取消的挂起函数还限定了在kotlinx.coroutines 中,这句话简直就是完美且准确!

注意啊,这个包名的第一个是kotlinx,后面是有x的,Kotlin的包名中有一个跟这个很相似的,是kotlin.coroutines,人家开发文档可没说kotlin.coroutines下面的挂起函数是可取消的!

比如suspendCoroutine就是不带x的包名下的函数,所以这个挂起函数并不可取消。相对地,实现相同功能又可取消的函数为suspendCancellableCoroutine,这个函数所在的包名是带x的。

这里suspendCoroutinesuspendCancellableCoroutine两个函数都是挂起函数,一个不可取消,一个可取消,另一方面也可以说明挂起函数并不总是支持取消协作的,取消的协作本质在挂起函数内部执行逻辑而与挂起函数无关。

这里,不妨再结合本文和一学就会的协程使用——基础篇(三)初遇协程取消中的代码,思考一下,协程的取消需要怎样的配合才能发挥取消的实际作用?

5. 样例工程代码

代码样例Demo,见Github:https://github.com/TeaCChen/CoroutineStudy

本文示例代码,如觉奇怪或啰嗦,其实为CancelStepTwoActivity.kt中的代码摘取主要部分说明,在demo代码当中,为提升细节内容,有更加多的封装和输出内容。

本文的页面截图示例如下:

image-5-1.png

一学就会的协程使用——基础篇

一学就会的协程使用——基础篇(一)协程启动

一学就会的协程使用——基础篇(二)线程切换

一学就会的协程使用——基础篇(三)初遇协程取消

一学就会的协程使用——基础篇(四)协程作用域

一学就会的协程使用——基础篇(五)再遇协程取消(本文)

一学就会的协程使用——基础篇(六)初识挂起

一学就会的协程使用——基础篇(七)初识结构化

一学就会的协程使用——基础篇(八)初识协程异常

一学就会的协程使用——基础篇(九)异常与supervisor

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容