从分页3的beta01
版本开始,当从RemoteMediator
刷新PagingData时,有时会发生上一代的旧APPEND
请求在刷新完成后仍在执行的情况。这似乎是从this commit读取的预期行为。当这种情况发生时,旧的APPEND
请求调用RemoteMediator
的load
方法,但使用了过时的PagingState
。如果我们在load函数中使用其中的信息,这个过时的PagingState
可能会导致错误和崩溃(例如,下面的代码片段使用lastItemOrNull
在数据库中查找项的RemoteKeys
)。这种破坏性的更改(也破坏了corresponding codelab)在发行说明中根本没有提到。我们应该如何处理这种情况?
下面是一个与beta01
断开的RemoteMediator示例。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)
}
}
}
发布于 2021-02-13 18:24:26
我和Dustin Lam和Yigit Boyar谈过了,显然,处理这个问题的最好方法是让prepend append不依赖于PagingState
。这意味着我们应该将远程键存储在与查询相关的表中,而不是存储在项级别上。
示例:
@Entity(tableName = "search_query_remote_keys")
data class SearchQueryRemoteKey(
@PrimaryKey val searchQuery: String,
val nextPageKey: Int
)
https://stackoverflow.com/questions/66169509
复制相似问题