You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

未本地保存时,服务器调用更新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的centers Flow
  • RequestCentersUseCase调用requestCenters()更新的是实例B的_centers MutableStateFlow

两个实例的Flow完全无关,自然监听方收不到更新通知。

其他排查点

  1. StateFlow更新验证:确认requestCenters()中_centers.value = centers.filterNotNull()确实执行了,且centers.filterNotNull()不是空列表(可添加日志输出验证)。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 04:06:20