如何解决使用带有 RemoteMediator 的 Paging 3 时如何在进程死亡后重置滚动位置
我已经使用 RemoteMediator
设置了带离线缓存的 Paging 3。进程死亡后,RecyclerView 立即恢复正确的滚动位置。然而,由于我们需要再次发送搜索查询,它会触发一个 LoadType.REFRESH
,它会从缓存中清除当前的搜索结果并用新值替换它们。这让我们回到了列表的开头。
我的远程中介:
private const val NEWS_STARTING_PAGE_INDEX = 1
class SearchNewsRemoteMediator(
private val searchQuery: String,private val newsDb: NewsArticleDatabase,private val newsApi: NewsApi
) : RemoteMediator<Int,NewsArticle>() {
private val newsArticleDao = newsDb.newsArticleDao()
override suspend fun load(
loadType: LoadType,state: PagingState<Int,NewsArticle>
): MediatorResult {
val page = when (loadType) {
LoadType.REFRESH -> {
val remoteKeys = getRemoteKeyClosestToCurrentPosition(state)
remoteKeys?.nextKey?.minus(1) ?: NEWS_STARTING_PAGE_INDEX
}
LoadType.PREPEND -> {
val remoteKeys = getRemoteKeyForFirstItem(state)
?: throw InvalidObjectException("Remote key should not be null for $loadType")
val prevKey = remoteKeys.prevKey
if (prevKey == null) {
return MediatorResult.Success(endOfPaginationReached = true)
}
remoteKeys.prevKey
}
LoadType.APPEND -> {
val remoteKeys = getRemoteKeyForLastItem(state)
if (remoteKeys == null || remoteKeys.nextKey == null) {
throw InvalidObjectException("Remote key should not be null for $loadType")
}
remoteKeys.nextKey
}
}
return try {
delay(2000)
val apiResponse = newsApi.searchNews(searchQuery,page,state.config.pageSize)
val serverSearchResults = apiResponse.articles
val endOfPaginationReached = serverSearchResults.isEmpty()
val bookmarkedArticles = newsArticleDao.getAllBookmarkedArticles().first()
val cachedBreakingNewsArticles = newsArticleDao.getCachedBreakingNews().first()
val searchResults = serverSearchResults.map { serverSearchResultArticle ->
val bookmarked = bookmarkedArticles.any { bookmarkedArticle ->
bookmarkedArticle.url == serverSearchResultArticle.url
}
val inBreakingNewsCache =
cachedBreakingNewsArticles.any { breakingNewsArticle ->
breakingNewsArticle.url == serverSearchResultArticle.url
}
NewsArticle(
title = serverSearchResultArticle.title,url = serverSearchResultArticle.url,urlToImage = serverSearchResultArticle.urlToImage,isBreakingNews = inBreakingNewsCache,isBookmarked = bookmarked,isSearchResult = true
)
}
newsDb.withTransaction {
if (loadType == LoadType.REFRESH) {
newsDb.searchRemoteKeyDao().clearRemoteKeys()
newsArticleDao.resetSearchResults()
newsArticleDao.deleteAllObsoleteArticles()
}
val prevKey = if (page == NEWS_STARTING_PAGE_INDEX) null else page - 1
val nextKey = if (endOfPaginationReached) null else page + 1
val remoteKeys = serverSearchResults.map { article ->
SearchRemoteKeys(article.url,prevKey,nextKey)
}
newsDb.searchRemoteKeyDao().insertAll(remoteKeys)
newsDb.newsArticleDao().insertAll(searchResults)
}
MediatorResult.Success(endOfPaginationReached)
} catch (exception: IOException) {
MediatorResult.Error(exception)
} catch (exception: HttpException) {
MediatorResult.Error(exception)
}
}
private suspend fun getRemoteKeyForLastItem(state: PagingState<Int,NewsArticle>): SearchRemoteKeys? =
state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
?.let { article ->
newsDb.searchRemoteKeyDao().getRemoteKeyFromArticleUrl(article.url)
}
private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int,NewsArticle>): SearchRemoteKeys? =
state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
?.let { article ->
newsDb.searchRemoteKeyDao().getRemoteKeyFromArticleUrl(article.url)
}
private suspend fun getRemoteKeyClosestToCurrentPosition(
state: PagingState<Int,NewsArticle>
): SearchRemoteKeys? =
state.anchorPosition?.let { position ->
state.closestItemToPosition(position)?.url?.let { articleUrl ->
newsDb.searchRemoteKeyDao().getRemoteKeyFromArticleUrl(articleUrl)
}
}
}
实例化它的存储库方法:
fun getSearchResults(query: String): Flow<PagingData<NewsArticle>> =
Pager(
config = PagingConfig(pageSize = 20,enablePlaceholders = false),remoteMediator = SearchNewsRemoteMediator(query,newsArticleDatabase,newsApi),pagingSourceFactory = { newsArticleDatabase.newsArticleDao().getSearchResultsPaged() }
).flow
触发查询的 ViewModel。 currentQuery
会在进程死亡后恢复,因此会立即使用旧查询调用 getSearchResults
。
class SearchNewsViewModel @ViewModelInject constructor(
private val repository: NewsRepository,@Assisted state: SavedStateHandle
) : ViewModel() {
private val currentQuery = state.getLiveData<String?>("currentQuery")
val newsArticles = currentQuery.switchMap { query ->
repository.getSearchResults(query).asLiveData().cachedIn(viewModelScope)
}
fun searchArticles(query: String) {
currentQuery.value = query
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。