我正在尝试使用Android Paging库显示条目列表。为此,我使用的是自定义PageDataSource,它从Room数据库读取数据,并在需要时从网络更新数据。为了用新数据更新本地数据库,我不使用边界回调,因为它没有提供每页更新数据的选项,而是仅在用户滚动到本地列表的底部时才提供。这意味着除非本地数据是第一页或最后一页的一部分,否则永远不会更新。
这是我的数据源:
class EntryPageDataSource(
private val entryDao: EntryDao,
private val service: AppService
) : PageKeyedDataSource<Int, EntryEntity>() {
companion object {
val lastSync = SparseArray<Instant>()
}
arrives
private val invalidationTracker =
object : InvalidationTracker.Observer(EntryEntity.TABLE_NAME) {
override fun onInvalidated(tables: MutableSet<String>) {
this@EntryPageDataSource.invalidate()
}
}
init {
//Track database updates in order to invalidate DataSource when new data
AppDatabase.getInstance().invalidationTracker.addObserver(invalidationTracker)
}
override fun invalidate() {
//Stop observing database updates as DataSource is invalid
GlobalScope.launch {
AppDatabase.getInstance().invalidationTracker.removeObserver(invalidationTracker)
}
super.invalidate()
}
override fun loadInitial(
params: LoadInitialParams<Int>,
callback: LoadInitialCallback<Int, EntryEntity>
) {
//Get first page from DB and notify callback
val data = entryDao.getPagedEntries(params.requestedLoadSize, 0)
callback.onResult(data, null, 1)
//Update data from network if needed
refreshPageData(params.requestedLoadSize, 0)
}
override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, EntryEntity>) {
//Get new page from DB and notify callback
callback.onResult(
entryDao.getPagedEntries(
params.requestedLoadSize,
params.key * params.requestedLoadSize
),
params.key + 1
)
//Update data from network if needed
refreshPageData(params.requestedLoadSize, params.key)
}
override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, EntryEntity>) {
}
private fun refreshPageData(size: Int, page: Int) {
//Do not request new data if we recently updated it
if (lastSync[page]?.isBefore(Instant.now().minusMillis(10 * 1000L)) == false) return
GlobalScope.launch {
runCatching {
service.getPagedEntries(page + 1, size)
}.onSuccess {
lastSync.put(page, Instant.now())
entryDao.upsert(it.map { it.toEntity() })
}.onFailure {
it.log()
}
}
}
}
对应的数据库功能:
@Query("SELECT * FROM ${EntryEntity.TABLE_NAME} ORDER BY ${EntryEntity.COLUMN_DATE} DESC LIMIT :limit OFFSET :offset")
abstract fun getPagedEntries(limit: Int, offset: Int): List<EntryEntity>
在ViewModel中,我正在使用以下代码生成此DataSource的实时数据:
LivePagedListBuilder(
entriesFactory.map { it.toEntry() },
Config(pageSize = 10, enablePlaceholders = false, initialLoadSizeHint = 10)
)
.setInitialLoadKey(0)
.build()
我遇到的问题是,在滚动列表时(由于从网络中获取了新页面,并且当前的DataSource无效),用户返回了列表的开头,而不是最后一个可见的页面。因此,没有流畅的滚动体验。如果使用PositionalDataSource,则会发生类似的行为。区别在于,对于PositionalDataSource,列表视图将自动滚动到最后加载的页面。基本上,这意味着列表将自动滚动到列表的最后一个元素。
如何在保持每页从网络更新本地数据的可能性的同时获得流畅的滚动体验?