Я настроил Paging 3 с автономным кэшированием с помощью RemoteMediator. После смерти процесса RecyclerView немедленно восстанавливает правильное положение прокрутки. Однако, поскольку нам нужно снова отправить поисковый запрос, он вызывает LoadType.REFRESH, который очищает текущие результаты поиска из кеша и заменяет их новыми значениями. Это возвращает нас к началу списка.
Мой RemoteMediator:
private const val NEWS_STARTING_PAGE_INDEX = 1
class SearchNewsRemoteMediator(
private val searchQuery: String,
private val newsDb: NewsArticleDatabase,
private val newsApi: NewsApi
) : RemoteMediator() {
private val newsArticleDao = newsDb.newsArticleDao()
override suspend fun load(
loadType: LoadType,
state: PagingState
): 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): SearchRemoteKeys? =
state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
?.let { article ->
newsDb.searchRemoteKeyDao().getRemoteKeyFromArticleUrl(article.url)
}
private suspend fun getRemoteKeyForFirstItem(state: PagingState): SearchRemoteKeys? =
state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
?.let { article ->
newsDb.searchRemoteKeyDao().getRemoteKeyFromArticleUrl(article.url)
}
private suspend fun getRemoteKeyClosestToCurrentPosition(
state: PagingState
): SearchRemoteKeys? =
state.anchorPosition?.let { position ->
state.closestItemToPosition(position)?.url?.let { articleUrl ->
newsDb.searchRemoteKeyDao().getRemoteKeyFromArticleUrl(articleUrl)
}
}
}
Метод репозитория, который его создает:
fun getSearchResults(query: String): Flow
> =
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("currentQuery")
val newsArticles = currentQuery.switchMap { query ->
repository.getSearchResults(query).asLiveData().cachedIn(viewModelScope)
}
fun searchArticles(query: String) {
currentQuery.value = query
}
}
Подробнее здесь: https://stackoverflow.com/questions/657 ... -3-with-re
Как сбросить положение прокрутки после смерти процесса при использовании Paging 3 с RemoteMediator ⇐ Android
Форум для тех, кто программирует под Android
1727447604
Anonymous
Я настроил Paging 3 с автономным кэшированием с помощью RemoteMediator. После смерти процесса RecyclerView немедленно восстанавливает правильное положение прокрутки. Однако, поскольку нам нужно снова отправить поисковый запрос, он вызывает LoadType.REFRESH, который очищает текущие результаты поиска из кеша и заменяет их новыми значениями. Это возвращает нас к началу списка.
Мой RemoteMediator:
private const val NEWS_STARTING_PAGE_INDEX = 1
class SearchNewsRemoteMediator(
private val searchQuery: String,
private val newsDb: NewsArticleDatabase,
private val newsApi: NewsApi
) : RemoteMediator() {
private val newsArticleDao = newsDb.newsArticleDao()
override suspend fun load(
loadType: LoadType,
state: PagingState
): 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): SearchRemoteKeys? =
state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
?.let { article ->
newsDb.searchRemoteKeyDao().getRemoteKeyFromArticleUrl(article.url)
}
private suspend fun getRemoteKeyForFirstItem(state: PagingState): SearchRemoteKeys? =
state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
?.let { article ->
newsDb.searchRemoteKeyDao().getRemoteKeyFromArticleUrl(article.url)
}
private suspend fun getRemoteKeyClosestToCurrentPosition(
state: PagingState
): SearchRemoteKeys? =
state.anchorPosition?.let { position ->
state.closestItemToPosition(position)?.url?.let { articleUrl ->
newsDb.searchRemoteKeyDao().getRemoteKeyFromArticleUrl(articleUrl)
}
}
}
Метод репозитория, который его создает:
fun getSearchResults(query: String): Flow
> =
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("currentQuery")
val newsArticles = currentQuery.switchMap { query ->
repository.getSearchResults(query).asLiveData().cachedIn(viewModelScope)
}
fun searchArticles(query: String) {
currentQuery.value = query
}
}
Подробнее здесь: [url]https://stackoverflow.com/questions/65762314/how-to-reset-the-scroll-position-after-process-death-when-using-paging-3-with-re[/url]
Ответить
1 сообщение
• Страница 1 из 1
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...
Мобильная версия