ViewModel中viewModelScope延迟协程在Activity关闭后不执行
问题原因
viewModelScope 是和ViewModel生命周期强绑定的协程作用域,它的默认取消时机是ViewModel被销毁时:也就是关联的Activity被永久关闭(用户按返回键、手动finish页面,非转屏/深色模式切换等配置变更场景)、ViewModel执行onCleared()方法的时候,作用域会自动调用cancel()取消所有在其中启动的未完成协程。
你遇到的场景就是3秒延迟还没走完,关闭Activity触发了ViewModel销毁,协程被直接取消,delay()之后的代码自然不会执行。
补充说明:如果是配置变更导致的Activity重建,ViewModel实例会被系统保留,viewModelScope中的协程不会被取消,这也是保持页面打开时协程能正常执行的原因。
修复方案
根据业务场景选择对应方案即可:
- 如果延迟执行的逻辑是UI相关操作(比如更新页面控件、弹出提示、跳转页面等),不需要修改现有代码。页面销毁后取消这类协程是
viewModelScope的设计初衷,能避免内存泄漏、访问已销毁View导致的空指针问题。 - 如果延迟执行的逻辑是不依赖UI的业务操作(比如埋点上报、本地缓存写入、数据同步等),要求即使页面关闭、ViewModel销毁也要执行,就不能使用
viewModelScope,需要选择生命周期更长的协程作用域启动任务:- 不要直接使用无管控的
GlobalScope,避免协程泄漏无法管控,建议在Application类中定义和应用进程同生命周期的自定义协程作用域:
// 进程级协程作用域,应用退出时才会销毁 val appScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)- 将需要脱离页面生命周期执行的任务放到该作用域中启动:
fun doAction() { appScope.launch { delay(3000) Log.i("Tag", "页面关闭后也可以正常打印日志") // 执行业务逻辑,注意此处如果需要Context必须使用Application的Context,不要持有Activity引用 } } - 不要直接使用无管控的
- 如果延迟任务需要跨进程执行、甚至应用被杀后重启也要保证触发,建议使用
WorkManager实现定时任务,不要用协程delay实现。
内容的提问来源于stack exchange,提问作者Artyom Danilin
相关产品推荐
相关产品推荐

