I am trying to use Paging 3 for my list. At first i use the PagingSource with only network data source and it is working properly. Then i tried to implement offline caching with RemoteMediator, but i couldnt get it work. There is 2 problem i experience with this:
- It constantly calling
LoadType.APPEND
even when the list doesnt get scrolled (idle) - It stucks on the page number 3 so i can not load other pages other than the initial 3
Here is my RemoteMediator code:
@ExperimentalPagingApi
class ProductRemoteMediator(
private val remoteDataSource: ProductRemoteDataSource,
private val localDataSource: ProductLocalDataSource,
) : RemoteMediator<Int, ProductEntity>() {
override suspend fun load(
loadType: LoadType,
state: PagingState<Int, ProductEntity>
): MediatorResult {
val page = when (loadType) {
REFRESH -> {
val remoteKeys = getRemoteKeyClosestToCurrentPosition(state)
remoteKeys?.nextKey?.minus(1) ?: STARTING_PAGE_INDEX
}
PREPEND -> {
val remoteKeys = getRemoteKeyForFirstItem(state)
val prevKey = remoteKeys?.prevKey
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
prevKey
}
APPEND -> {
val remoteKeys = getRemoteKeyForLastItem(state)
val nextKey = remoteKeys?.nextKey
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
nextKey
}
}
return try {
// i know from this log and interceptor that it calls constantly and infinitely
Timber.d("loadType=$loadType")
Timber.d("page=$page, size=${state.config.pageSize}")
val products = remoteDataSource.getProductsAsBuyer(
page = page,
size = state.config.pageSize,
).map { it.mapToEntityModel() }
val endOfPaginationReached = products.isEmpty()
Timber.d("endOfPaginationReached=$endOfPaginationReached")
localDataSource.cacheProductTransaction {
// clear all tables in the database
if (loadType == REFRESH) {
localDataSource.clearRemoteKeys()
localDataSource.clearCachedProducts()
}
val prevKey = if (page == STARTING_PAGE_INDEX) null else page - 1
val nextKey = if (endOfPaginationReached) null else page + 1
val keys = products.map {
RemoteKeys(productId = it.id, prevKey = prevKey, nextKey = nextKey)
}
localDataSource.insertRemoteKeys(keys)
localDataSource.cacheAllProducts(products)
}
MediatorResult.Success(endOfPaginationReached = endOfPaginationReached)
} catch (exception: IOException) {
MediatorResult.Error(exception)
} catch (exception: HttpException) {
MediatorResult.Error(exception)
} catch (exception: Exception) {
MediatorResult.Error(exception)
}
}
private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, ProductEntity>): RemoteKeys? {
return state.pages
.lastOrNull { it.data.isNotEmpty() }
?.data
?.lastOrNull()
?.let { repo ->
localDataSource.getRemoteKeysId(repo.id)
}
}
private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, ProductEntity>): RemoteKeys? {
return state.pages
.firstOrNull { it.data.isNotEmpty() }
?.data
?.firstOrNull()
?.let { repo ->
localDataSource.getRemoteKeysId(repo.id)
}
}
private suspend fun getRemoteKeyClosestToCurrentPosition(
state: PagingState<Int, ProductEntity>
): RemoteKeys? {
return state.anchorPosition?.let { position ->
state.closestItemToPosition(position)?.id?.let { repoId ->
localDataSource.getRemoteKeysId(repoId)
}
}
}
}
And here is my Pager in the repository
override fun getProductsAsBuyer(): Flow<PagingData<ProductEntity>> {
return Pager(
config = PagingConfig(pageSize = NETWORK_PAGE_SIZE),
remoteMediator = ProductRemoteMediator(
remoteDataSource = remoteDataSource,
localDataSource = localDataSource,
),
pagingSourceFactory = {
localDataSource.getCachedProducts()
}
).flow
}
Any help is appreciated.
Additional Info: i use Jetpack Compose and collectAsLazyPagingItems()
to display it.