kotlin 协程 supervisorscope {} 运行崩溃解决方法-kb88凯时官网登录

来自:网络
时间:2024-06-09
阅读:
免费资源网,https://freexyz.cn/

kotlin 协程 supervisorscope {} 运行崩溃解决

前言

简单介绍supervisorscope函数,它用于创建一个使用了 supervisorjob 的 coroutinescope,
该作用域的特点:抛出的异常,不会 连锁取消 同级协程和父协程。

看过很多 supervisorscope {} 文档的使用,我照抄一摸一样的代码,运行就崩溃,最后找到了解决方法,应该是kotlin版本更新做过改动,当前我使用的是 androidx.core:core-ktx:1.9.0

解决方法

需要将coroutineexceptionhandler,作为参数,才有效果,不然会崩溃。

    private fun test() {
        // 原来的写法,现在会崩溃
//        runblocking {
//            log.d("tag", "start")
//            launch {
//                delay(100)
//                log.d("tag", "task from runblocking")
//            }
//            supervisorscope {
//                val firstchild = launch {
//                    log.d("tag", "first child")
//                    throw assertionerror("first child is cancelled")
//                }
//                val secondchild = launch {
//                    log.d("tag", "second child")
//                }
//                log.d("tag", "cancelling supervisor")
//            }
//            log.d("tag", "end")
//        }
        // 最新的写法
        runblocking {
            log.d("tag", "start")
            launch {
                delay(100)
                log.d("tag", "task from runblocking")
            }
            supervisorscope {
                // 需要将coroutineexceptionhandler,作为参数,才有效果,不然会崩溃
                val firstchild = launch(coroutineexceptionhandler { _, _ -> }) {
                    log.d("tag", "first child")
                    throw assertionerror("first child is cancelled")
                }
                val secondchild = launch {
                    log.d("tag", "second child")
                }
                log.d("tag", "cancelling supervisor")
            }
            log.d("tag", "end")
        }
    }

补充:

kotlin 协程异常处理

import kotlinx.coroutines.*
import java.net.url
suspend fun fetchresponse(code: int, delay: int) = coroutinescope {
    val deferred: deferred = async {
            .readtext()
        }
    try {
        val response = deferred.await()
        println(response)
    } catch(ex: cancellationexception) {
        println("${ex.message} for fetchresponse $code")
    }
}
runblocking {
    val handler = coroutineexceptionhandler {_, ex ->
        println("exception handled: ${ex.message}")
    }
    val job = launch(dispatchers.io   supervisorjob()   handler) {
        // 协程1
        launch{fetchresponse(202, 1000)} // 协程2
        launch{fetchresponse(404, 2000)} // 协程3
        launch{fetchresponse(200, 3000)} // 协程4
    }
    job.join()
}

如上的运行结果如下所示

202 accepted
parent job is cancelling for fetchresponse 200
exception handled: http://httpstat.us/404?sleep=2000

分析:

一、fetchresponse使用的是coroutinescope来包起来,那么如果有协程的异常则该函数不会自己处理会向上传播;如果不想让协程的异常向外传播则可以使用supervisorscope

二、fetchresponse内部函数使用的是async/await, 并且try/catch没有将async包起来,那么如果async发生了异常会发生什么事情呢?

1、async作为 根协程(coroutinescope或者 supervisorscope的直接子协程),异常不会自动抛出;它会在你调用await的时候才抛出所以这里的try/catch只是将await包起来即可

2、那么既然await的异常已经被捕获了,为何404还会向上抛出异常导致其他协程的取消呢?这是因为当async发生异常的时候会立即把异常抛出给父节点,因为父节点是coroutinescope所以继续向上抛出直到协程1,然后协程1将所有的子协程取消

那么问题来了:1中说的async不会抛出异常,2中又说会立即抛出异常;那么是否是自相矛盾呢?

async抛出的异常try/catch是没法捕获的,它会向父协程传播异常(即父子协程之间的异常传播,而不是代码级别的异常传播所以try/catch捕获不到;曾经使用catch(ex: exception) 尝试捕获确实没有捕获成功)

综上所述,async跟await都是有抛出异常,只是他们抛出的方式不一样,async是父子协程之间的异常传播而await是代码逻辑的异常传播可被try/catch捕获到

那么问题来了:async不能被try/catch捕获到,该怎样捕获呢?

这里的launch使用了coroutineexceptionhandler进行捕获;当异常传到协程1的时候,继续向上传播;因为协程1的父协程是supervisorjob(为何协程1的父协程是supervisorjob可以见)所以需要协程1自己处理;因为协程1的launch中包含了handler的异常捕获则会使用该异常类进行处理

这里launch可以使用coroutineexceptionhandler进行捕获,那么async可以使用coroutineexceptionhandler进行捕获么? 答案是不可以的,async对应的await需要使用try/catch进行捕获

注意:coroutinescope构建器中抛出的异常,或由协程创建的协程中抛出的异常,不会被 try/catch捕获!

具体的可参考:

fq版(英文版):

免费资源网,https://freexyz.cn/
返回顶部
顶部
网站地图