Paging Library问题:数据库存储数据未触发UI更新
解决Paging Library中BoundaryCallback写入数据库后UI不更新的问题
我太懂你现在的头疼了——用JetPack Paging Library做Demo,从RandomUser API拉取用户列表,BoundaryCallback明明把网络数据存进了数据库,但UI的LiveData/Rx监听就是没反应,非得重启APP才显示数据。我之前也踩过这个坑,咱们一步步把它搞定。
核心问题拆解
你当前自定义的PageKeyedDataSource是一次性实例:它初始化时从数据库拉一次数据,之后数据库更新了,它完全感知不到变化,也不会重新加载数据,导致PagedList没更新,UI自然没反应。
最省心的解决方案:用Room内置的Paging支持
Room已经原生适配了Paging Library,能自动帮你处理数据库变化后的DataSource刷新,这是最优解,步骤如下:
1. 改造Dao,返回DataSource.Factory
把原来的分页查询方法换成Room提供的原生分页接口,直接返回DataSource.Factory,注意必须加稳定的排序规则:
@Dao interface UserProfileDao { // 必须指定排序,Paging需要稳定的排序避免数据混乱 @Query("SELECT * FROM user_profiles ORDER BY indexPageNumber ASC") fun getUserProfilesPaged(): DataSource.Factory<Int, UserProfile> }
2. 更新ViewModel的分页配置
不再用自定义的UserProfileDataSourceFactory,直接用Room返回的Factory构建PagedList:
private val config = PagedList.Config.Builder() .setEnablePlaceholders(false) .setPrefetchDistance(UserProfileDataSource.PAGE_SIZE / 2) .setPageSize(UserProfileDataSource.PAGE_SIZE) .build() val userProfiles: LiveData<PagedList<UserProfile>> = LivePagedListBuilder( userProfileDao.getUserProfilesPaged(), // 替换为Room的Factory config ) .setBoundaryCallback(UserProfileBoundaryCallback(randomUserRepository, userProfileDao)) .build()
这样一来,当BoundaryCallback把数据写入数据库后,Room会自动触发DataSource重新创建,PagedList更新,你的Fragment里的observe就能立刻收到新数据,UI直接刷新。
如果你坚持用自定义DataSource的方案
要是不想调整Room的Dao结构,那得手动触发DataSource的刷新:
1. 改造DataSourceFactory,添加刷新能力
让Factory持有DataSource的引用,并提供手动失效的方法:
class UserProfileDataSourceFactory(private val userDao: UserDao) : DataSource.Factory<Int, UserProfile>() { private val _dataSource = MutableLiveData<UserProfileDataSource>() val dataSource: LiveData<UserProfileDataSource> = _dataSource override fun create(): DataSource<Int, UserProfile> { val source = UserProfileDataSource(userDao) _dataSource.postValue(source) return source } // 手动触发DataSource失效,强制重新创建 fun invalidateDataSource() { _dataSource.value?.invalidate() } }
2. 在ViewModel中持有Factory引用
val dataSourceFactory = UserProfileDataSourceFactory(userProfileDao) val userProfiles: LiveData<PagedList<UserProfile>> = LivePagedListBuilder( dataSourceFactory, config ) .setBoundaryCallback(UserProfileBoundaryCallback(randomUserRepository, userProfileDao)) .build()
3. 在BoundaryCallback写入数据库后触发刷新
override fun onZeroItemsLoaded() { userRepository.getUserProfiles(0, UserProfileDataSource.PAGE_SIZE) .subscribeOn(Schedulers.io()) .subscribe(object: DataSourceSubscriber<List<UserProfile>>() { override fun onResultNext(userProfiles: List<UserProfile>) { userDao.storeUserProfiles(userProfiles) // 通知Factory刷新DataSource viewModel.dataSourceFactory.invalidateDataSource() } }) } override fun onItemAtEndLoaded(itemAtEnd: UserProfile) { userRepository.getUserProfiles(itemAtEnd.indexPageNumber + 1, UserProfileDataSource.PAGE_SIZE) .subscribeOn(Schedulers.io()) .subscribe(object: DataSourceSubscriber<List<UserProfile>>() { override fun onResultNext(userProfiles: List<UserProfile>) { userDao.storeUserProfiles(userProfiles) viewModel.dataSourceFactory.invalidateDataSource() } }) }
额外注意事项
- 数据库操作必须在IO线程执行,你当前用
subscribeOn(Schedulers.io())是正确的,别改成UI线程 - Paging必须依赖稳定的排序规则,否则会出现数据重复、遗漏或乱序的问题
- 如果用RxPagedListBuilder,搭配Room的
DataSource.Factory会自动生成Observable<PagedList<UserProfile>>,数据库变化时会自动发射新的PagedList,无需额外处理
内容的提问来源于stack exchange,提问作者4gus71n
相关产品推荐
相关产品推荐

