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

Android多网络调用场景下LiveData/MediatorLiveData链式操作高效实现方法

实现方案

完整代码实现

class TodoViewModel(
    private val todoRepository: TodoRepository,
    private val todoDao: TodoDao,
    private val dispatcherProvider: DispatcherProvider
) : ViewModel() {
    private val todoListMediator = MediatorLiveData<NetworkResult<List<TodoEntity>>>()
    // 保存当前已添加的数据源,用于后续移除避免重复观察
    private var currentTodoSource: LiveData<NetworkResult<List<TodoEntity>>>? = null
    val todoResponse: LiveData<NetworkResult<List<TodoEntity>>> get() = todoListMediator

    fun loadTodoList() {
        viewModelScope.launch(dispatcherProvider.main) {
            // 先移除旧的数据源,避免重复回调产生数据混乱
            currentTodoSource?.let { oldSource ->
                todoListMediator.removeSource(oldSource)
            }
            // 获取新的todo列表数据源
            val newTodoSource = todoRepository.getTodoList()
            currentTodoSource = newTodoSource

            todoListMediator.addSource(newTodoSource) { result ->
                // 优先透传原始状态(加载中/失败)给UI层,保证状态同步及时
                todoListMediator.value = result

                // 仅在请求成功返回列表时走后续补充字段逻辑
                if (result is NetworkResult.Success) {
                    // 切换到IO线程执行耗时的网络+数据库操作
                    viewModelScope.launch(dispatcherProvider.io) {
                        val originalList = result.data
                        // 1. 并发请求每个todo的额外字段,相比串行请求大幅提升效率
                        val updatedList = originalList.map { todo ->
                            async {
                                runCatching {
                                    // 传入当前todo的唯一标识请求对应的额外字段
                                    todoRepository.getPendingTodoValues(todo.id)
                                }.fold(
                                    onSuccess = { extraResult ->
                                        if (extraResult is NetworkResult.Success) {
                                            // 补充额外字段,生成新的Todo实体
                                            todo.copy(pendingValues = extraResult.data)
                                        } else {
                                            // 单个请求失败兜底,保留原数据
                                            todo
                                        }
                                    },
                                    onFailure = {
                                        // 捕获异常,避免单个请求失败导致整个列表处理流程崩溃
                                        todo
                                    }
                                )
                            }
                        }.awaitAll() // 等待所有todo的额外字段请求完成

                        // 2. 将补充完字段的完整列表存入本地数据库
                        todoDao.insertAll(updatedList)

                        // 3. 切回主线程更新MediatorLiveData的最终值
                        withContext(dispatcherProvider.main) {
                            todoListMediator.value = NetworkResult.Success(updatedList)
                        }
                    }
                }
            }
        }
    }
}

关键实现技巧说明

  • 旧数据源主动移除:每次添加新的数据源前先移除旧的,避免多个数据源同时回调导致数据混乱,也能避免内存泄漏
  • 状态优先透传:拿到原始结果后先直接赋值给MediatorLiveData,保证加载中、失败等状态可以第一时间同步到UI,不会出现长时间空白
  • 耗时操作线程隔离:网络请求、数据库操作都在IO调度器执行,不会阻塞主线程导致ANR
  • 并发请求优化:使用async+awaitAll同时发起多个todo的额外字段请求,相比串行请求可以大幅减少总耗时
  • 单请求异常兜底:每个额外字段的请求都加了异常捕获,单个todo的请求失败不会影响整个列表的处理流程,可根据需求自定义失败后的处理逻辑(比如重试、标记异常状态等)
  • 线程安全的LiveData赋值:最终更新LiveData的时候切回主线程,避免后台线程直接更新LiveData导致的异常

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 12:57:03