未本地保存时,服务器调用更新Repository中Flow无通知问题排查
问题原因分析与解决方案
问题背景
我希望在不本地保存数据的前提下,通过服务器调用更新Repository中的Flow。ViewModel中有两个launch函数:一个发起服务器请求并返回AppError,另一个监听Flow以接收服务器返回的数据。目前列表已成功下载,但ViewModel中的launchGetCenters()函数仅能收到初始空列表,未收到Flow的更新通知。
相关代码
Fragment代码
private fun collectState() { with(viewModel.state) { diff(viewLifecycleOwner, {it.centers}) { centers -> log("Centros: $centers") } } } fun <T, U> Flow<T>.diff(lifecycleOwner: LifecycleOwner, mapf: (T) -> U, body: (U) -> Unit) { lifecycleOwner.launchAndCollect( flow = map(mapf).distinctUntilChanged(), body = body ) } fun <T> LifecycleOwner.launchAndCollect( flow: Flow<T>, state: Lifecycle.State = Lifecycle.State.STARTED, body: (T) -> Unit ) { lifecycleScope.launch { this@launchAndCollect.repeatOnLifecycle(state) { flow.collect(body) } } }
ViewModel代码
init { launchGetCenters() } fun onUiReady() { launchRequestCentersUseCase() } private fun launchGetCenters() { viewModelScope.launch { getCentersUseCase() .catch { cause -> _state.update { it.copy(appError = cause.toError()) } } .collect { centers -> _state.update { it.copy(centers = centers) } } } } private fun launchRequestCentersUseCase() { viewModelScope.launch { _state.value = _state.value.copy(progress = true) val error = requestCentersUseCase() _state.update { _state.value.copy(progress = false, appError = error) } } }
Repository代码
private var _centers: MutableStateFlow<List<Center>> = MutableStateFlow(emptyList()) val centers = _centers.asStateFlow() suspend fun requestCenters(): AppError? { val response: Either<AppError, List<Center?>> = remoteDataSource.requestCenters() response.fold(ifLeft = { return it }) { centers -> _centers.value = centers.filterNotNull() } return null }
UseCase代码
class GetCentersUseCase @Inject constructor(private val repository: RemoteRepository) { operator fun invoke(): Flow<List<Center>> = repository.centers }
class RequestCentersUseCase @Inject constructor(private val repository: RemoteRepository) { suspend operator fun invoke(): AppError? = repository.requestCenters() }
核心原因:Repository实例不唯一
这是触发该问题的最常见原因:如果你的依赖注入(DI)框架(如Hilt、Dagger)没有将RemoteRepository配置为单例,那么GetCentersUseCase和RequestCentersUseCase会获取到两个完全独立的RemoteRepository实例:
GetCentersUseCase监听的是实例A的centersFlowRequestCentersUseCase调用requestCenters()更新的是实例B的_centersMutableStateFlow
两个实例的Flow完全无关,自然监听方收不到更新通知。
其他排查点
- StateFlow更新验证:确认
requestCenters()中_centers.value = centers.filterNotNull()确实执行了,且centers.filterNotNull()不是空列表(可添加日志输出验证)。 - ViewModel订阅状态:虽然
launchGetCenters()在init中启动,但如果viewModelScope被意外取消(比如ViewModel提前销毁),也会导致订阅终止,但你提到能收到初始值,该可能性较低。
解决方案
1. 将Repository配置为单例
以Hilt为例,在RemoteRepository的构造函数上添加@Singleton注解:
@Singleton class RemoteRepository @Inject constructor(private val remoteDataSource: RemoteDataSource) { // ... 现有代码 }
其他DI框架需对应配置单例规则,确保所有UseCase获取到同一个Repository实例。
2. 验证实例一致性
在Repository和ViewModel中添加日志,确认更新和监听的是同一个实例:
// Repository中添加日志 private var _centers: MutableStateFlow<List<Center>> = MutableStateFlow(emptyList()) val centers = _centers.asStateFlow() init { Log.d("Repository", "实例创建:${hashCode()}") } suspend fun requestCenters(): AppError? { Log.d("Repository", "当前实例:${hashCode()}") val response: Either<AppError, List<Center?>> = remoteDataSource.requestCenters() response.fold(ifLeft = { return it }) { centers -> val filtered = centers.filterNotNull() Log.d("Repository", "更新列表:$filtered") _centers.value = filtered } return null }
// ViewModel的launchGetCenters中添加日志 private fun launchGetCenters() { viewModelScope.launch { getCentersUseCase() .catch { cause -> _state.update { it.copy(appError = cause.toError()) } } .collect { centers -> Log.d("ViewModel", "收到列表更新:$centers") _state.update { it.copy(centers = centers) } } } }
运行后查看日志,确认Repository实例的hashCode一致,且更新日志和接收日志对应。
内容的提问来源于stack exchange,提问作者Iñaki B
相关产品推荐
相关产品推荐

