2

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:

  1. It constantly calling LoadType.APPEND even when the list doesnt get scrolled (idle)
  2. 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.

wiryadev
  • 1,089
  • 1
  • 11
  • 33

0 Answers0