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

MVVM架构下LiveData协程首次正常后续失效 求技术支持

MVVM架构下LiveData后续调用无更新的问题排查与修复

嘿,我一眼就看出问题所在了——你每次调用loadYgosuData()的时候,都会新建一个LiveData实例!第一次调用时,你的观察者订阅了这个新的LiveData,自然能收到数据;但后续再调用方法,返回的是另一个完全不同的LiveData对象,原来的观察者根本没订阅它,当然就收不到更新啦。

下面是具体的修复步骤,咱们一步步来:

1. 把LiveData改成ViewModel的成员变量

不要每次调用方法都创建新的LiveData,而是在ViewModel里持有一个固定的实例,这样所有的更新都会推送给同一个观察者。修改后的代码大概是这样:

class YourYgosuViewModel : ViewModel() {
    // 用MutableLiveData保存内部可修改的数据,对外暴露不可变的LiveData
    private val _ygosuData = MutableLiveData<ArrayList<YgosuData>>()
    val ygosuData: LiveData<ArrayList<YgosuData>> get() = _ygosuData

    fun loadYgosuData() {
        Log.d(TAG, "loadYgosuData()...")
        viewModelScope.launch {
            Log.d(TAG, "viewModelScope running....")
            try {
                // 网络请求放在IO线程
                val element = withContext(Dispatchers.IO) {
                    Jsoup.connect("https://www.ygosu.com/community/real_article")
                        .get()
                        .select("div.board_wrap tbody tr")
                }
                // 直接更新已有LiveData的值,而不是返回新的LiveData
                _ygosuData.value = setYgosuData(element)
            } catch (e: Exception) {
                Log.e(TAG, "加载YGOSU数据失败", e)
                // 这里可以加个错误状态的LiveData,通知View层加载失败
            }
        }
    }

    private fun setYgosuData(tmpDoc: Elements?) : ArrayList<YgosuData> {
        val tmpList = ArrayList<YgosuData>()
        tmpDoc?.forEach { e ->
            val title = e.select("td.tit a").text()
            val name = e.select("td.name a").text()
            val read = e.select("td.read").text()
            val date = e.select("td.date").text()
            val url = e.select("td.tit a").attr("href").toString()
            
            tmpList.add(YgosuData(title, name, read, date, url))
        }
        Log.d(TAG, "여기2 ${tmpList.toString()}")
        return tmpList
    }
}

2. 在View层只订阅一次LiveData

不管你调用多少次loadYgosuData(),只要订阅的是同一个LiveData实例,就能收到每次的更新。在Activity或Fragment里这么写:

// 初始化ViewModel(这里用ViewModelProvider,根据你的项目情况调整)
val viewModel = ViewModelProvider(this)[YourYgosuViewModel::class.java]

// 只需要订阅一次,后续更新都会自动通知
viewModel.ygosuData.observe(this) { dataList ->
    // 拿到新数据后更新UI,比如刷新RecyclerView
    yourAdapter.submitList(dataList)
}

// 第一次加载或者需要刷新的时候,直接调用方法就行
binding.refreshButton.setOnClickListener {
    viewModel.loadYgosuData()
}

额外的小优化建议

  • 尽量用List<YgosuData>代替ArrayList<YgosuData>,更符合面向接口的编程习惯,也更灵活。
  • 可以加个加载状态的LiveData,比如_isLoading = MutableLiveData<Boolean>(),在开始加载时设为true,加载完成或失败时设为false,让View层显示加载动画或者错误提示。
  • 网络请求记得加超时或者异常捕获,避免因为网络问题直接崩溃。

内容的提问来源于stack exchange,提问作者임세준

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 22:57:36