Jetpack Lifecycle2.4.0下ActivityLifecycleCallbacks中MutableStateFlow报后台线程setValue错误
根因分析
该报错本质是后台线程执行了只能在主线程调用的操作,结合你升级到Lifecycle 2.4.0+使用repeatOnLifecycle的场景,具体触发逻辑如下:
- 你在
getMembership方法中用ioContext(IO线程调度器)启动协程执行requestSingle方法 requestSingle内的Flow未指定flowOn切换调度器,所有的Flow发射、收集操作都继承外层的IO线程调度器,在IO线程执行MutableStateFlow.emit更新状态- Lifecycle 2.4.0+的
repeatOnLifecycle不会主动将上游的非主线程事件切换到主线程,会直接继承上游协程上下文执行collect回调 - 如果你的
asImmutable()封装逻辑中包含StateFlow转LiveData的操作,IO线程更新StateFlow会触发LiveData.setValue在后台线程执行,直接抛出该错误;就算没有转LiveData,你在collect回调中直接调用view.onMembershipSuccess等UI操作,也会因为IO线程操作UI抛出异常
修复方案
你可以任选以下一种方案修复,推荐同时做线程防护避免后续出现同类问题:
方案1:调整Flow的调度器,确保状态更新在主线程
修改requestSingle方法,将业务执行和状态更新的线程隔离:
suspend inline fun <R> BaseViewModel.requestSingle( state: MutableStateFlow<State.Single<R>>, delay: Long = Constant.MEDIUM_DELAY, crossinline block: suspend () -> R, ) = mutex.withLock { flow { emit(State.Single.Loading()) val result = block.invoke() delay(delay) emit(State.Single.Success(result)) }.flowOn(Dispatchers.IO) // 网络请求、延迟等业务逻辑切到IO线程执行 .catch { withContext(Dispatchers.Main.immediate) { state.emit(State.Single.Failed(it)) } } .collect { // 状态更新统一切到主线程执行 withContext(Dispatchers.Main.immediate) { state.emit(it) } } }
方案2:修改协程启动调度器
viewModelScope默认使用主线程调度器,无需额外指定IO线程启动,网络请求等耗时操作内部会自行切换线程:
fun getMembership() = viewModelScope.launch { // 移除ioContext,默认主线程启动 requestSingle(_membership) { useCase.getMembership() } }
补充优化:给UI回调加主线程防护
修改observeSingle方法,强制collect回调在主线程执行,避免后续其他场景出现线程问题:
suspend inline fun <R> StateFlow<State.Single<R>>.observeSingle( coroutineScope: CoroutineScope, crossinline block: suspend (State.Single<R>) -> Unit, ) { // 强制UI回调在主线程执行 coroutineScope.launch(Dispatchers.Main.immediate) { collect { block.invoke(it) } } }
另外注意你当前的代码逻辑存在空指针风险:只有当Activity是AppCompatActivity类型时才会初始化mViewModel,如果启动的Activity不是该类型,onActivityStarted中调用mViewModel?.value?.getMembership()会出现空调用,建议补充边界判断。
内容的提问来源于stack exchange,提问作者Joseph Sanjaya
相关产品推荐
相关产品推荐

