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

Room数据库更新时LiveData未触发,ViewPager双Fragment数据不同步

解决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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 10:02:51