Kotlin之协程(二)取消

简介

介绍

此篇文章主要介绍了kotlin的取消深入的介绍,如果之前没有接触过协程,可以
参考上一篇内容初始中的内容。

参考文档

  1. 谷歌开发者
  2. Kotlin文档

文章目录

Kotlin之协程(一)初识
Kotlin之协程(二)取消

协程之取消深入

1.概述

简答的取消在上篇初识中已经有所介绍,不再赘述,这篇继续来深入了解协程的取消。

2.取消异常

代码如下
    val job = launch(Dispatchers.Default) {
           try {
               var index = 0
               while (index <10) {
                   delay(200)
                   Log.d(Constants.TAG, "当前位置:${index++}")
               }
           }catch (e:Exception){
               Log.d(Constants.TAG,"异常:${e.message}")
           }
        }
        delay(1000)
        Log.d(Constants.TAG, "要取消了")
        job.cancelAndJoin()
        Log.d(Constants.TAG, "取消完成")
日志结果如下
2021-09-16 11:12:20.710 12740-12789/demo.demo.democoroutines D/Coroutines: 当前位置:0
2021-09-16 11:12:20.912 12740-12789/demo.demo.democoroutines D/Coroutines: 当前位置:1
2021-09-16 11:12:21.113 12740-12789/demo.demo.democoroutines D/Coroutines: 当前位置:2
2021-09-16 11:12:21.314 12740-12789/demo.demo.democoroutines D/Coroutines: 当前位置:3
2021-09-16 11:12:21.506 12740-12740/demo.demo.democoroutines D/Coroutines: 要取消了
2021-09-16 11:12:21.508 12740-12789/demo.demo.democoroutines D/Coroutines: 异常:StandaloneCoroutine was cancelled
2021-09-16 11:12:21.509 12740-12740/demo.demo.democoroutines D/Coroutines: 取消完成
结果分析:

1、在delay延时打印时候取消会先抛出JobCancellationException异常,异常信息:StandaloneCoroutine was cancelled。
2、如果只是cancelAndJoin()则日志如下,如果只是cancel()则异常信息会在完成之后,因为join会占用协程。

3.未能取消的协程

代码如下:
val job = launch(Dispatchers.Default) {
           try {
               var index = 0
               val startTime = System.currentTimeMillis()
               var nextPrintTime = startTime
               while (index <10) {
                   if (System.currentTimeMillis() >= nextPrintTime){
                       Log.d(Constants.TAG, "当前位置:${index++}")
                       nextPrintTime += 200L
                   }
               }
           }catch (e:Exception){
               Log.d(Constants.TAG,"异常:${e}")
           }
        }
        delay(1000)
        Log.d(Constants.TAG, "要取消了")
        job.cancelAndJoin()
        Log.d(Constants.TAG, "取消完成")
日志如下:
2021-09-16 11:20:06.746 13747-13798/demo.demo.democoroutines D/Coroutines: 当前位置:0
2021-09-16 11:20:06.946 13747-13798/demo.demo.democoroutines D/Coroutines: 当前位置:1
2021-09-16 11:20:07.146 13747-13798/demo.demo.democoroutines D/Coroutines: 当前位置:2
2021-09-16 11:20:07.346 13747-13798/demo.demo.democoroutines D/Coroutines: 当前位置:3
2021-09-16 11:20:07.546 13747-13798/demo.demo.democoroutines D/Coroutines: 当前位置:4
2021-09-16 11:20:07.745 13747-13747/demo.demo.democoroutines D/Coroutines: 要取消了
2021-09-16 11:20:07.746 13747-13798/demo.demo.democoroutines D/Coroutines: 当前位置:5
2021-09-16 11:20:07.946 13747-13798/demo.demo.democoroutines D/Coroutines: 当前位置:6
2021-09-16 11:20:08.146 13747-13798/demo.demo.democoroutines D/Coroutines: 当前位置:7
2021-09-16 11:20:08.346 13747-13798/demo.demo.democoroutines D/Coroutines: 当前位置:8
2021-09-16 11:20:08.546 13747-13798/demo.demo.democoroutines D/Coroutines: 当前位置:9
2021-09-16 11:20:08.548 13747-13747/demo.demo.democoroutines D/Coroutines: 取消完成

结果分析:
1、由现象可以可出,协程在取消了之后,仍然还在进行任务,如果为while(true)则会一直执行,因为没有delay,cpu一直在进行运算占用,则此时即使调用了calcelAndJoin()任务仍然会继续执行,取消不了。
2、cancelAndJoin()会占用协程,所以直到最后"取消完成"才打印出来,如果是cancel()则会和"要取消了"一起打印出来。

4.解决未能取消的协程问题

思路:

协程在取消之后状态会发生变化,已经不处在活跃状态,所以我们在循环判断的时候注意判断协程的状态,如果占用的任务不是循环形式则可以定时进行判断协程的状态。

上述3中的未能取消的问题解决,代码如下:
val job = launch(Dispatchers.Default) {
           try {
               var index = 0
               val startTime = System.currentTimeMillis()
               var nextPrintTime = startTime
               while (index <10 && isActive) {//此处根据isActive,处在active状态再进行继续
                   if (System.currentTimeMillis() >= nextPrintTime){
                       Log.d(Constants.TAG, "当前位置:${index++}")
                       nextPrintTime += 200L
                   }
               }
           }catch (e:Exception){
               Log.d(Constants.TAG,"异常:${e}")
           }
        }
        delay(1000)
        Log.d(Constants.TAG, "要取消了")
        job.cancelAndJoin()
        Log.d(Constants.TAG, "取消完成")
日志如下:
2021-09-16 11:29:16.938 14216-14313/demo.demo.democoroutines D/Coroutines: 当前位置:0
2021-09-16 11:29:17.136 14216-14313/demo.demo.democoroutines D/Coroutines: 当前位置:1
2021-09-16 11:29:17.336 14216-14313/demo.demo.democoroutines D/Coroutines: 当前位置:2
2021-09-16 11:29:17.536 14216-14313/demo.demo.democoroutines D/Coroutines: 当前位置:3
2021-09-16 11:29:17.736 14216-14313/demo.demo.democoroutines D/Coroutines: 当前位置:4
2021-09-16 11:29:17.936 14216-14313/demo.demo.democoroutines D/Coroutines: 当前位置:5
2021-09-16 11:29:17.945 14216-14216/demo.demo.democoroutines D/Coroutines: 要取消了
2021-09-16 11:29:17.950 14216-14216/demo.demo.democoroutines D/Coroutines: 取消完成
结果分析:

1、可以看出在cancelAndJoin()之后任务已经不再继续执行(即取消成功了),因为此时协程的isActive已经不是true了。
2、第二种解决办法(定时检测状态),可以看出也能达到取消的效果:

代码如下:
 var isAlive = true
        val job = launch(Dispatchers.Default) {
           try {
               var index = 0
               val startTime = System.currentTimeMillis()
               var nextPrintTime = startTime
               while (index <10 && isAlive) {//此处根据isActive,处在active状态再进行继续
                   if (System.currentTimeMillis() >= nextPrintTime){
                       Log.d(Constants.TAG, "当前位置:${index++}")
                       nextPrintTime += 200L
                   }
               }

           }catch (e:Exception){
               Log.d(Constants.TAG,"异常:${e}")
           }
        }
        launch(Dispatchers.IO) {
            while (isAlive){
                delay(200)
                isAlive  = job.isActive
                Log.d(Constants.TAG,"当前协程状态:$isAlive")
            }
        }
        delay(1000)
        Log.d(Constants.TAG, "要取消了")
        job.cancel()
        Log.d(Constants.TAG, "取消完成")
日志如下:
2021-09-16 11:51:46.957 17264-17300/demo.demo.democoroutines D/Coroutines: 当前位置:0
2021-09-16 11:51:47.156 17264-17300/demo.demo.democoroutines D/Coroutines: 当前位置:1
2021-09-16 11:51:47.162 17264-17301/demo.demo.democoroutines D/Coroutines: 当前协程状态:true
2021-09-16 11:51:47.356 17264-17300/demo.demo.democoroutines D/Coroutines: 当前位置:2
2021-09-16 11:51:47.363 17264-17301/demo.demo.democoroutines D/Coroutines: 当前协程状态:true
2021-09-16 11:51:47.556 17264-17300/demo.demo.democoroutines D/Coroutines: 当前位置:3
2021-09-16 11:51:47.564 17264-17302/demo.demo.democoroutines D/Coroutines: 当前协程状态:true
2021-09-16 11:51:47.756 17264-17300/demo.demo.democoroutines D/Coroutines: 当前位置:4
2021-09-16 11:51:47.765 17264-17302/demo.demo.democoroutines D/Coroutines: 当前协程状态:true
2021-09-16 11:51:47.956 17264-17300/demo.demo.democoroutines D/Coroutines: 当前位置:5
2021-09-16 11:51:47.960 17264-17264/demo.demo.democoroutines D/Coroutines: 要取消了
2021-09-16 11:51:47.961 17264-17264/demo.demo.democoroutines D/Coroutines: 取消完成
2021-09-16 11:51:47.966 17264-17302/demo.demo.democoroutines D/Coroutines: 当前协程状态:false

5.人为设置任务不能取消

1.我们先看一个现象

代码如下:
 val job = launch {
            try {
                repeat(100) {
                    delay(200)
                    Log.d(Constants.TAG, "任务执行:$it")
                }
            } catch (e: Exception) {
                Log.d(Constants.TAG, "异常:${e.message}")
            } finally {
                Log.d(Constants.TAG, "finally")
                delay(1000)
                Log.d(Constants.TAG, "finally_delay")
            }
        }
        delay(2000)
        Log.d(Constants.TAG, "要取消了")
        job.cancelAndJoin()
        Log.d(Constants.TAG, "取消完成")
日志如下:
2021-09-16 13:45:12.886 20483-20483/demo.demo.democoroutines D/Coroutines: 任务执行:0
2021-09-16 13:45:13.090 20483-20483/demo.demo.democoroutines D/Coroutines: 任务执行:1
2021-09-16 13:45:13.294 20483-20483/demo.demo.democoroutines D/Coroutines: 任务执行:2
2021-09-16 13:45:13.498 20483-20483/demo.demo.democoroutines D/Coroutines: 任务执行:3
2021-09-16 13:45:13.704 20483-20483/demo.demo.democoroutines D/Coroutines: 任务执行:4
2021-09-16 13:45:13.909 20483-20483/demo.demo.democoroutines D/Coroutines: 任务执行:5
2021-09-16 13:45:14.114 20483-20483/demo.demo.democoroutines D/Coroutines: 任务执行:6
2021-09-16 13:45:14.318 20483-20483/demo.demo.democoroutines D/Coroutines: 任务执行:7
2021-09-16 13:45:14.522 20483-20483/demo.demo.democoroutines D/Coroutines: 任务执行:8
2021-09-16 13:45:14.690 20483-20483/demo.demo.democoroutines D/Coroutines: 要取消了
2021-09-16 13:45:14.702 20483-20483/demo.demo.democoroutines D/Coroutines: 异常:StandaloneCoroutine was cancelled
2021-09-16 13:45:14.702 20483-20483/demo.demo.democoroutines D/Coroutines: finally
2021-09-16 13:45:14.707 20483-20483/demo.demo.democoroutines D/Coroutines: 取消完成
现象分析:

1、我们可以看到,取消了之后排除了异常,但是finally也执行了,但是finally_delay却没有执行,原因是因为在job取消了之后已经不能再挂起了,在delay(1000)的时候抛出了异常,所以finally_delay未能执行。

delay(1000)异常验证并执行finally_delay:

将上述finally代码改造如下:

Log.d(Constants.TAG, "finally")
                try {
                    delay(1000)
                }catch (e:Exception){
                    Log.d(Constants.TAG, "finally_delay异常:${e.message}")
                }finally {
                    Log.d(Constants.TAG, "finally_delay")
                }

部分日志如下:

2021-09-16 13:53:50.347 20753-20753/demo.demo.democoroutines D/Coroutines: 要取消了
2021-09-16 13:53:50.360 20753-20753/demo.demo.democoroutines D/Coroutines: 异常:StandaloneCoroutine was cancelled
2021-09-16 13:53:50.360 20753-20753/demo.demo.democoroutines D/Coroutines: finally
2021-09-16 13:53:50.361 20753-20753/demo.demo.democoroutines D/Coroutines: finally_delay异常:StandaloneCoroutine was cancelled
2021-09-16 13:53:50.361 20753-20753/demo.demo.democoroutines D/Coroutines: finally_delay
2021-09-16 13:53:50.363 20753-20753/demo.demo.democoroutines D/Coroutines: 取消完成

可以看出在finally之后delay(1000)挂起的时候抛出了异常,异常信息:StandaloneCoroutine was cancelled,而在下一个finally中的finally_delay也执行了,但是没有进行挂起延时而已。

在finally中实现挂起延时利器withContext(NonCancellable) {……}

代码如下:

 val job = launch {
            try {
                repeat(100) {
                    delay(200)
                    Log.d(Constants.TAG, "任务执行:$it")
                }
            } catch (e: Exception) {
                Log.d(Constants.TAG, "异常:${e.message}")
            } finally {
                withContext(NonCancellable){
                    Log.d(Constants.TAG, "finally")
                    delay(1000)
                    Log.d(Constants.TAG, "finally_delay")
                }
            }
        }
        delay(2000)
        Log.d(Constants.TAG, "要取消了")
        job.cancelAndJoin()
        Log.d(Constants.TAG, "取消完成")

日志如下:

2021-09-16 13:58:39.077 20983-20983/demo.demo.democoroutines D/Coroutines: 任务执行:0
2021-09-16 13:58:39.284 20983-20983/demo.demo.democoroutines D/Coroutines: 任务执行:1
2021-09-16 13:58:39.487 20983-20983/demo.demo.democoroutines D/Coroutines: 任务执行:2
2021-09-16 13:58:39.693 20983-20983/demo.demo.democoroutines D/Coroutines: 任务执行:3
2021-09-16 13:58:39.897 20983-20983/demo.demo.democoroutines D/Coroutines: 任务执行:4
2021-09-16 13:58:40.101 20983-20983/demo.demo.democoroutines D/Coroutines: 任务执行:5
2021-09-16 13:58:40.305 20983-20983/demo.demo.democoroutines D/Coroutines: 任务执行:6
2021-09-16 13:58:40.509 20983-20983/demo.demo.democoroutines D/Coroutines: 任务执行:7
2021-09-16 13:58:40.713 20983-20983/demo.demo.democoroutines D/Coroutines: 任务执行:8
2021-09-16 13:58:40.876 20983-20983/demo.demo.democoroutines D/Coroutines: 要取消了
2021-09-16 13:58:40.884 20983-20983/demo.demo.democoroutines D/Coroutines: 异常:StandaloneCoroutine was cancelled
2021-09-16 13:58:40.890 20983-20983/demo.demo.democoroutines D/Coroutines: finally
2021-09-16 13:58:41.897 20983-20983/demo.demo.democoroutines D/Coroutines: finally_delay
2021-09-16 13:58:41.901 20983-20983/demo.demo.democoroutines D/Coroutines: 取消完成

可以看出finally_delay实现了延时执行。

6.超时取消

代码如下:
 launch {
            try {
                withTimeout(2000){
                    repeat(100) {
                        delay(200)
                        Log.d(Constants.TAG, "任务执行:$it")
                    }
                }
            } catch (e: Exception) {
                Log.d(Constants.TAG, "异常:${e}")
            }
        }
日志如下:
2021-09-16 14:10:38.187 22160-22160/demo.demo.democoroutines D/Coroutines: 任务执行:0
2021-09-16 14:10:38.388 22160-22160/demo.demo.democoroutines D/Coroutines: 任务执行:1
2021-09-16 14:10:38.589 22160-22160/demo.demo.democoroutines D/Coroutines: 任务执行:2
2021-09-16 14:10:38.789 22160-22160/demo.demo.democoroutines D/Coroutines: 任务执行:3
2021-09-16 14:10:38.990 22160-22160/demo.demo.democoroutines D/Coroutines: 任务执行:4
2021-09-16 14:10:39.190 22160-22160/demo.demo.democoroutines D/Coroutines: 任务执行:5
2021-09-16 14:10:39.391 22160-22160/demo.demo.democoroutines D/Coroutines: 任务执行:6
2021-09-16 14:10:39.593 22160-22160/demo.demo.democoroutines D/Coroutines: 任务执行:7
2021-09-16 14:10:39.798 22160-22160/demo.demo.democoroutines D/Coroutines: 任务执行:8
2021-09-16 14:10:39.990 22160-22160/demo.demo.democoroutines D/Coroutines: 异常:kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 2000 ms
现象分析:

1、可以看出在2000ms之后超时抛出了异常,并且任务停止了运行。

总结

本文主要介绍了协程各种取消的处理及各种取消的适用情况,对一些情况的解决思路进行分析,
当然这也是本人的学习笔记,希望也能对大家有那么一点点的帮助或者启发,我就很开心了。
当然了本人也是在学习与理解的过程中记录与理解难免有一定的认知局限性,如果发现有什
么问题,欢迎指正,谢谢。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353

推荐阅读更多精彩内容