自版本以来beta01
的 Paging 3,当从 a 刷新 PagingData 时RemoteMediator
,有时会发生旧的APPEND
刷新完成后,仍会执行上一代的请求。这似乎是预期的行为这次提交 https://android.googlesource.com/platform/frameworks/support/+/e754df9ed34341e5aa71e26427244951089e854b%5E%21/paging/common/src/main/kotlin/androidx/paging/RemoteMediatorAccessor.kt?fbclid=IwAR1ED_0_f-1KFQy0TXCnmWvihBTh8SsTY4EaygzKM-90NhZ2i7J9jCvxE00。
当这种情况发生时,老APPEND
请求调用RemoteMediator
's load
方法但过时了PagingState
。这个过时的PagingState
如果我们在加载函数中使用其中的信息,可能会导致错误和崩溃(例如,下面的代码片段使用lastItemOrNull
找到RemoteKeys
对于数据库中的项目)。这一重大变化(也打破了对应的Codelab https://developer.android.com/codelabs/android-paging#19)在发行说明中根本没有提及。我们应该如何处理这个问题?
这是一个 RemoteMediator 的示例,它与beta01
. The getRemoteKeyForLastItem
方法可以返回null
(因为旧PagingState
正在查找先前删除的数据库条目REFRESH
)导致InvalidObjectException
被扔掉。
private const val GITHUB_STARTING_PAGE_INDEX = 1
@OptIn(ExperimentalPagingApi::class)
class GithubRemoteMediator(
private val query: String,
private val service: GithubService,
private val repoDatabase: RepoDatabase
) : RemoteMediator<Int, Repo>() {
override suspend fun load(loadType: LoadType, state: PagingState<Int, Repo>): MediatorResult {
val page = when (loadType) {
LoadType.REFRESH -> GITHUB_STARTING_PAGE_INDEX
LoadType.PREPEND -> return MediatorResult.Success(true)
LoadType.APPEND -> {
// this can run with an outdated PagingState from the previous RemoteMediator instance, causing the Exception to be thrown
val remoteKeys = getRemoteKeyForLastItem(state)
if (remoteKeys == null || remoteKeys.nextKey == null) {
throw InvalidObjectException("Remote key should not be null for $loadType")
}
remoteKeys.nextKey
}
}
val apiQuery = query + IN_QUALIFIER
try {
val apiResponse = service.searchRepos(apiQuery, page, state.config.pageSize)
val repos = apiResponse.items
val endOfPaginationReached = repos.isEmpty()
repoDatabase.withTransaction {
if (loadType == LoadType.REFRESH) {
repoDatabase.remoteKeysDao().clearRemoteKeys()
repoDatabase.reposDao().clearRepos()
}
val prevKey = if (page == GITHUB_STARTING_PAGE_INDEX) null else page - 1
val nextKey = if (endOfPaginationReached) null else page + 1
val keys = repos.map {
RemoteKeys(repoId = it.id, prevKey = prevKey, nextKey = nextKey)
}
repoDatabase.remoteKeysDao().insertAll(keys)
repoDatabase.reposDao().insertAll(repos)
}
return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached)
} catch (exception: IOException) {
return MediatorResult.Error(exception)
} catch (exception: HttpException) {
return MediatorResult.Error(exception)
}
}
private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, Repo>): RemoteKeys? {
return state.pages.lastOrNull() { it.data.isNotEmpty() }?.data?.lastOrNull()
?.let { repo ->
repoDatabase.remoteKeysDao().remoteKeysRepoId(repo.id)
}
}
}
我与 Dustin Lam 和 Yigit Boyar 进行了交谈,显然,处理此问题的最佳方法是在前面添加一个附加项,而不依赖于PagingState
。这意味着我们应该将远程密钥存储在与查询相关的表中,而不是存储在项目级别上。
Exmaple:
@Entity(tableName = "search_query_remote_keys")
data class SearchQueryRemoteKey(
@PrimaryKey val searchQuery: String,
val nextPageKey: Int
)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)