子协程失败时viewModelScope为何自行取消?与SupervisorJob特性矛盾求解答
问题原因分析
你对viewModelScope的SupervisorJob理解有个关键偏差:viewModelScope的根SupervisorJob只负责隔离直接在它下面启动的顶级协程——比如你分别调用viewModelScope.launch{...}和viewModelScope.launch{...},这两个协程是兄弟关系,一个失败不会牵连另一个。
但你的代码里,是在同一个viewModelScope.launch(...)的协程内部,又用launch{}启动了子协程。这时候,这个内部子协程的父Job是当前协程的普通Job(而非SupervisorJob),当它抛出未捕获的异常时,异常会向上传播给父协程,直接导致父协程被取消——父协程里的repo.fetchLocalData().collect{...}自然也会跟着终止,根本没机会执行。
验证与解决思路
- 为什么
runCatching{}包裹后没问题?因为runCatching会内部捕获异常,阻止它向上传播到父协程,父协程就能正常执行后续的collect逻辑。 - 要实现你预期的“子协程失败不影响父协程”,有两种常用方式:
- 用
supervisorScope包裹内部子协程,让内部协程的失败仅局限在自身范围内:
private fun fetchData() { viewModelScope.launch(ioDispatcher + exceptionHandler) { supervisorScope { launch { repo.refreshData() } } repo.fetchLocalData().collect { data -> liveData.postValue(data) } } }- 给内部的
launch单独绑定异常处理器,让异常在子协程内部被处理,不向上传播:
private fun fetchData() { viewModelScope.launch(ioDispatcher + exceptionHandler) { launch(exceptionHandler) { repo.refreshData() } repo.fetchLocalData().collect { data -> liveData.postValue(data) } } } - 用
内容的提问来源于stack exchange,提问作者X09
相关产品推荐
相关产品推荐

