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
相关产品推荐
相关产品推荐

