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,提问作者임세준
相关产品推荐
相关产品推荐

