Room数据库更新时LiveData未触发,ViewPager双Fragment数据不同步
看起来你遇到的核心问题是:当一个Fragment修改了数据库中的收藏状态后,另一个Fragment的列表没有自动感知到数据变化并刷新。这是因为你当前的实现依赖**主动调用loadFavoriteKafani()**来获取数据,而数据库更新后没有自动通知所有观察者。
下面是具体的优化方案,利用Room的LiveData特性来实现自动同步:
第一步:修改Dao层,使用LiveData返回查询结果
Room原生支持将查询结果包装为LiveData,当数据库数据发生变化时,它会自动通知所有订阅的观察者,这是实现自动同步的关键。
@Query("SELECT * FROM ${Constants.KAFANI_TABLE_NAME} WHERE isFavorite = 1") fun getFavoriteKafani(): LiveData<MutableList<Kafana>> // 把Single改成LiveData @Query("UPDATE ${Constants.KAFANI_TABLE_NAME} SET isFavorite = :isFavorite WHERE id = :id") fun setFavourite(id: Int, isFavorite: Int) // 这个方法保持不变
第二步:调整Repository层,传递LiveData
Repository只需要直接返回Dao的LiveData即可,不需要再包装成Single:
fun getFavoriteKafani(): LiveData<MutableList<Kafana>> { return kafanaDao.getFavoriteKafani() } fun setKafanaFavorite(kafana: Kafana, isFavorite: Int): Completable = Completable.fromCallable { kafanaDao.setFavourite(kafana.id, isFavorite) }
第三步:重构ViewModel,自动监听数据变化
ViewModel不需要再手动调用loadFavoriteKafani()来拉取数据,而是直接暴露LiveData,并监听它的变化来更新空状态:
class YourViewModel(private val kafanaRepository: KafanaRepository) : ViewModel() { // 直接暴露LiveData,让Fragment观察 val kafaniList: LiveData<MutableList<Kafana>> = kafanaRepository.getFavoriteKafani() val isFavoriteEmpty = MutableLiveData<Boolean>() private val compositeDisposable = CompositeDisposable() init { // 自动监听收藏列表的变化,更新空状态 compositeDisposable.add( kafaniList.distinctUntilChanged() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe { list -> isFavoriteEmpty.value = list.isEmpty() val tag = Constants.KAFANA_APP_TAG if (list.isEmpty()) { Timber.d("$tag Favorite list is empty") } else { Timber.d("$tag Favorite list has ${list.size} items") } } ) } fun setFavorite(kafana: Kafana, isFavorite: Int) { compositeDisposable.addAll( kafanaRepository.setKafanaFavorite(kafana, isFavorite) .compose(scheduler.getSchedulersForCompletable()) .subscribeBy( onComplete = { Timber.d("${Constants.KAFANA_APP_TAG} Updated favorite status for ${kafana.name}") // 不需要手动通知更新,Room会自动触发LiveData回调 }, onError = { Timber.d("${Constants.KAFANA_APP_TAG} setFavorite failed: ${it.localizedMessage}") } ) ) } override fun onCleared() { super.onCleared() compositeDisposable.clear() // 清理订阅,避免内存泄漏 } }
第四步:修改Fragment,优化列表刷新逻辑
Fragment只需要观察ViewModel的LiveData即可,当数据变化时自动刷新。另外建议复用Adapter而不是每次重新创建,提升性能:
private fun setupFavoriteListObserver() { viewModel.kafaniList.observe(viewLifecycleOwner, Observer<MutableList<Kafana>> { list -> list?.let { val sortedList = it.sortedWith(compareBy({ !it.isSponsored }, { it.name })).toMutableList() if (::kafanaAdapter.isInitialized) { // 如果Adapter已存在,直接更新数据 kafanaAdapter.updateData(sortedList) } else { kafanaAdapter = KafanaAdapter(sortedList, this, 2) binder.rvKafani.adapter = kafanaAdapter } } }) // 观察空状态,处理空视图逻辑 viewModel.isFavoriteEmpty.observe(viewLifecycleOwner, Observer { isEmpty -> // 比如显示/隐藏空布局:binder.emptyView.visibility = if (isEmpty) View.VISIBLE else View.GONE }) }
记得给你的KafanaAdapter添加一个更新数据的方法:
fun updateData(newList: MutableList<Kafana>) { this.dataList.clear() this.dataList.addAll(newList) notifyDataSetChanged() // 或者用DiffUtil做更高效的局部刷新 }
为什么这样能解决问题?
当你在任意一个Fragment调用setFavorite()修改数据库后,Room会自动检测到数据变化,并触发getFavoriteKafani()返回的LiveData更新。所有观察这个LiveData的Fragment(包括ViewPager里的另一个Fragment)都会收到新数据,从而自动刷新列表。
另外,使用viewLifecycleOwner代替this来观察LiveData,可以避免Fragment销毁后仍持有引用导致的内存泄漏。
内容的提问来源于stack exchange,提问作者jlively

