从beta01
Paging 3 版本开始,在从 a 刷新 PagingData 时,有时会出现刷新完成后仍然执行上一代RemoteMediator
的旧请求。APPEND
这似乎是从这个 commit读取的预期行为。发生这种情况时,旧APPEND
请求会调用RemoteMediator
'sload
方法,但使用过时的PagingState
. PagingState
如果我们在加载函数中使用其中的信息(例如,下面的代码片段用于在数据库lastItemOrNull
中查找项目),那么这个过时的可能会导致错误和崩溃。RemoteKeys
这个突破性的变化(这也打破了相应的代码实验室) 根本没有在发行说明中提及。我们应该如何处理这件事?
这是一个与beta01
. 该getRemoteKeyForLastItem
方法可以返回null
(因为 oldPagingState
正在寻找在上一个中删除的数据库条目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)
}
}
}