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

如何合并LiveData?UDF架构下State类更新UI失败排查

排查MediatorLiveData映射界面状态失败的问题

你的代码里有几个关键错误导致_categories无法正确接收数据更新,下面逐一拆解并给出修复方案:

核心错误点

1. MediatorLiveData是局部对象,未被持有

你在getCategories的协程里创建了MediatorLiveData,但它只是个临时变量,代码执行完这个代码块后就会被垃圾回收,根本没法持续监听result(也就是getListCategoryUseCase()返回的LiveData)的变化。而且**getListCategoryUseCase()返回的是LiveData,不需要在协程里调用**——LiveData本身就是异步数据流,协程在这里完全多余。

2. 错误的异常捕获逻辑

getListCategoryUseCase()返回LiveData,调用它不会抛出IOException,所以你的catch块永远不会触发。LiveData的数据流异常应该在观察者中处理,而不是直接调用方法时捕获。

3. 状态更新未重置loading状态

初始设置loading = true后,后续拿到数据时没有把loading设为false,会导致进度条一直显示,界面状态异常。

修复后的ViewModel代码

class MenuViewModel @Inject constructor(
    private val loadCategoryUseCase: LoadCategoryUseCase,
    private val getListCategoryUseCase: GetListCategoryUseCase
) : ViewModel() {

    private val _categories = MutableLiveData<StateMenuFragment>()
    val categories: LiveData<StateMenuFragment>
        get() = _categories

    val categoryTest = getListCategoryUseCase() // 测试变量

    // 把MediatorLiveData声明为成员变量,确保持有引用
    private val categoryMediator = MediatorLiveData<List<Category>>()

    init {
        viewModelScope.launch {
            loadCategoryUseCase()
        }
        getCategories()
    }

    private fun getCategories() {
        // 初始状态:显示加载中
        _categories.value = StateMenuFragment(loading = true)
        
        // 监听getListCategoryUseCase返回的LiveData
        categoryMediator.addSource(getListCategoryUseCase()) { categoryList ->
            // 更新状态:关闭加载,填充数据
            val updatedState = _categories.value?.copy(
                loading = false,
                result = categoryList
            ) ?: StateMenuFragment(
                loading = false,
                result = categoryList
            )
            _categories.value = updatedState
        }
    }

    // 可选:如果需要处理LiveData的异常,可以在观察者中处理,或者在UseCase层封装错误状态
}

优化后的Fragment代码

去掉重复的订阅逻辑,只保留对categories的监听即可:

class MenuFragment : Fragment() {

    private fun updateState() {
        viewModel.categories.observe(viewLifecycleOwner) { state ->
            // 先默认隐藏进度条,再根据状态调整
            binding.progressBar.visibility = View.GONE
            
            if (state.loading) {
                binding.progressBar.visibility = View.VISIBLE
            }
            
            if (state.error.isError) {
                Toast.makeText(requireContext(), state.error.errorMessage, Toast.LENGTH_SHORT).apply {
                    setGravity(Gravity.CENTER, 0, 0)
                }.show()
            }
            
            menuAdapter.submitList(state.result)
        }
    }
}

额外建议

  • 把StateMenuFragment里的result改成val,符合单向数据流的不可变原则,避免意外修改状态。
  • 如果需要处理数据加载失败的情况,建议在GetListCategoryUseCase中返回包含错误状态的密封类,或者直接在LiveData中传递错误信息,而不是靠捕获异常(因为Room查询不会抛出IOException,除非是数据库操作的底层错误)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 23:15:35