使用viewModelScope(含SupervisorJob)为何第三个launch协程被取消?
问题原因分析
你的代码里第三个launch没打印"API 3"的核心原因是:外层viewModelScope.launch(handler)创建的是普通Job协程,而非SupervisorJob。
虽然viewModelScope的根协程上下文用的是SupervisorJob(),但你内部的三个launch是外层这个普通Job协程的子协程,并非viewModelScope的直接子协程。而普通Job的特性是:只要有一个子协程抛出未捕获的异常,整个Job会被立即取消,所有关联的子协程都会被终止。
对应你的代码流程:
- 三个子协程同时启动,分别开始执行
- 1秒后第一个协程打印"API 1"
- 1.5秒后第二个协程抛出
NullPointerException - 外层的普通Job检测到子协程异常,立刻取消所有子协程(包括第三个还在执行
delay(2000)的协程) - 外层的
CoroutineExceptionHandler捕获到异常并打印信息,但此时第三个协程已经被终止,无法完成后续的打印
解决办法
要让内部子协程的异常不互相牵连,有两种常用方案:
方案1:直接将子协程作为viewModelScope的直接子协程
去掉外层的launch(handler),让三个子协程直接挂在viewModelScope的SupervisorJob下,同时给需要处理异常的协程单独设置异常处理器:
fun test() { viewModelScope.launch { launch(handler) { println("API 1 launch executing") apiCall("API 1", 1000) } launch(handler) { println("API 2 launch executing") delay(1500) throw NullPointerException("Exception") } launch { println("API 3 launch executing") apiCall("API 3", 2000) } delay(4000) println("API no api its ended.") } }
方案2:用supervisorScope包裹内部子协程
在外层launch里用supervisorScope创建一个局部的SupervisorJob作用域,这样内部的子协程异常只会终止自身,不会影响其他协程:
fun test() { viewModelScope.launch(handler) { supervisorScope { launch { println("API 1 launch executing") apiCall("API 1", 1000) } launch { println("API 2 launch executing") delay(1500) throw NullPointerException("Exception") } launch { println("API 3 launch executing") apiCall("API 3", 2000) } } delay(4000) println("API no api its ended.") } }
这两种方案都能让第三个协程正常执行并打印"API 3",同时异常会被正确捕获。
内容的提问来源于stack exchange,提问作者Hisham Muneer
相关产品推荐
相关产品推荐

