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

如何实现返回Flow的用例/仓库及异常通知(单一数据源+远程拉取)

解决方案:通知Flow观察者远程同步错误

针对你的场景,有两种主流且实用的方案来处理远程同步失败时的状态通知,下面分别说明并给出代码示例:


方案一:用密封类统一封装状态(推荐)

这种方式把「加载中、数据成功、同步错误」三种状态和数据绑定在一起,观察者可以在同一个Flow收集逻辑里处理所有情况,UI状态更统一。

步骤1:定义状态密封类

sealed class UserState {
    object Loading : UserState() // 同步开始时的加载状态
    data class Success(val users: List<User>) : UserState() // 数据加载成功(本地或同步后)
    data class SyncError(val message: String, val exception: Throwable? = null) : UserState() // 远程同步失败
}

步骤2:修改Repository接口

interface UserRepository {
    fun fetchUsersFlow(): Flow<UserState>
}

步骤3:实现Repository逻辑

class UserRepositoryImpl(
    private val localUserDataSource: LocalUserDatasource,
    private val remoteUserDataSource: RemoteUserDatasource,
    private val coroutineScope: CoroutineScope
) : UserRepository {

    override fun fetchUsersFlow(): Flow<UserState> = flow {
        // 1. 先发送加载状态
        emit(UserState.Loading)
        
        // 2. 先发送本地已有的缓存数据
        val initialLocalUsers = localUserDataSource.getUsersSync() // 假设本地有同步获取数据的方法
        emit(UserState.Success(initialLocalUsers))
        
        // 3. 尝试远程同步
        runCatching {
            remoteUserDataSource.fetchUsers()
        }.onSuccess { remoteUsers ->
            // 同步成功:更新本地数据库,本地Flow会自动推送最新数据
            localUserDataSource.updateUsers(remoteUsers)
        }.onFailure { exception ->
            // 同步失败:发送错误状态
            val errorMsg = if (exception is IOException) "网络连接失败" else "同步用户数据失败"
            emit(UserState.SyncError(errorMsg, exception))
        }
        
        // 4. 持续监听本地数据库的变化,推送最新数据
        emitAll(localUserDataSource.fetchUsersFlow().map { UserState.Success(it) })
    }
}

观察者侧使用示例

// 在ViewModel或UI层
scope.launch {
    userRepository.fetchUsersFlow().collect { state ->
        when (state) {
            is UserState.Loading -> showLoading()
            is UserState.Success -> showUsers(state.users)
            is UserState.SyncError -> showErrorToast(state.message)
        }
    }
}

方案二:用单独的错误事件Flow

如果不想修改原有数据Flow的类型,可以在Repository里新增一个专门用于发送错误事件的Flow,观察者同时监听数据和错误事件。

步骤1:修改Repository实现

class UserRepositoryImpl(
    private val localUserDataSource: LocalUserDatasource,
    private val remoteUserDataSource: RemoteUserDatasource,
    private val coroutineScope: CoroutineScope
) : UserRepository {
    // 定义错误事件Flow,replay=0表示只通知当前订阅的观察者
    private val _syncErrors = MutableSharedFlow<Throwable>(replay = 0)
    val syncErrors: Flow<Throwable> = _syncErrors

    override fun fetchUsersFlow(): Flow<List<User>> {
        coroutineScope.launch {
            runCatching {
                remoteUserDataSource.fetchUsers()
            }.onSuccess { remoteUsers ->
                localUserDataSource.updateUsers(remoteUsers)
            }.onFailure { exception ->
                // 发送错误事件
                _syncErrors.emit(exception)
            }
        }
        return localUserDataSource.fetchUsersFlow()
    }
}

观察者侧使用示例

// 在ViewModel或UI层
scope.launch {
    userRepository.fetchUsersFlow().collect { users ->
        showUsers(users)
    }
}

scope.launch {
    userRepository.syncErrors.collect { exception ->
        val errorMsg = if (exception is IOException) "网络连接失败" else "同步用户数据失败"
        showErrorToast(errorMsg)
    }
}

两种方案对比

  • 密封类方案:状态逻辑集中,UI层可以统一处理加载、数据、错误状态,适合需要完整状态反馈的场景。
  • 单独错误Flow方案:不需要修改原有数据Flow结构,适合只需要在错误时做简单提示的场景,但需要同时维护两个Flow的收集逻辑。

额外注意:尽量使用和生命周期绑定的协程Scope(如ViewModel的viewModelScope),避免内存泄漏;可以根据异常类型(如IOException)给出更精准的错误提示。

内容的提问来源于stack exchange,提问作者Augusto Carmo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 16:32:42