Sunday, 14 August 2022

Paging 3 library is not requesting my API after first usage

I'm trying to paginate my REST API with the paging 3 library, specifically with the RemoteMediator class.
What I want to achieve is that my local database is being used for the initial presentation of my UI, which gets updated reactively with a rest call if there are any changes in the shown data.

The strange behaviour that I have right now is, that the pagination only works like expected for the first time: Pages are loaded incrementally (meaning a REST call is fired) when the user scrolls the RecyclerView.

After having data in my local database, there is only a single rest call in the beginning (with the initial load size) and at the end where like 4 requests are fired successively. So it does not request my API incrementally like the first time, it just fires all requests at the end.

Is this behaviour normal ? Or is this a bug ?

My RemoteMediator code:

@ExperimentalPagingApi
class PageableRemoteMediator<T : Any>(
    private val pageKeyId: String,
    private val apiRequestFun: suspend (PageKey?, Int) -> ApiResult<Page<T>>,
    private val entityPersisterFun: suspend (Page<T>) -> Unit,
    application: Application
) : RemoteMediator<Int, T>() {
    private val db = DatabaseProvider.getDatabase(application)
    private val pageKeyDao = db.pageKeyDao()

    override suspend fun initialize(): InitializeAction {
        return InitializeAction.LAUNCH_INITIAL_REFRESH
    }

    override suspend fun load(loadType: LoadType, state: PagingState<Int, T>): MediatorResult {
        val pageKey = getPageKeyOrNull(loadType)
        
        /**
         * If the next pageKey is null and loadType is APPEND, then the pagination end is reached.
         * Also a prepend is seen as the end, since the RecyclerView always start from the beginning after a refresh.
         */
        if ((loadType == LoadType.APPEND && pageKey?.next == null) || loadType == LoadType.PREPEND) {
            return MediatorResult.Success(endOfPaginationReached = true)
        }

        val limit = if (loadType == LoadType.REFRESH) {
            state.config.initialLoadSize
        } else {
            state.config.pageSize
        }

        return when (val result = apiRequestFun(pageKey, limit)) {
            is ApiResult.Success -> {
                db.withTransaction {
                    val page = result.value

                    removeAllPageKeysIfRefreshRequested(loadType)

                    // This function just does an @Insert query for the specific type in a Room DAO class.
                    // I'm not calling something like a BaseDAO function, since sometimes I need to use insert queries with multiple models in the args.
                    // E.g. @Insert insertUniversityWithStudents(university : University, students : List<Student>) in UniversityDAO
                    entityPersisterFun(page)
                    saveNewPageKey(page)

                    val endOfPaginationReached = page.cursor.next == null
                    MediatorResult.Success(endOfPaginationReached)
                }
            }
            is ApiResult.Failed -> {
                MediatorResult.Error(result.getException())
            }
        }
    }

    private suspend fun getPageKeyOrNull(loadType: LoadType): PageKey? {
        return if (loadType != LoadType.REFRESH) {
            pageKeyDao.findByKey(pageKeyId)
        } else {
            null
        }
    }

    private suspend fun removeAllPageKeysIfRefreshRequested(loadType: LoadType) {
        if (loadType == LoadType.REFRESH) {
            pageKeyDao.removeAllByKey(pageKeyId)
        }
    }

    private suspend fun saveNewPageKey(entityPage: Page<*>) {
        val key = PageKey(pageKeyId, entityPage.cursor.next, entityPage.cursor.prev)
        pageKeyDao.asyncInsert(key)
    }
}

EDIT: I found this code snippet in the Android codelab

// clear all tables in the database
if (loadType == LoadType.REFRESH) {
   repoDatabase.remoteKeysDao().clearRemoteKeys()
   repoDatabase.reposDao().clearRepos()
}

Wouldn't this delete all my local data after an app restart ? Im not getting why I should use the RemoteMediator at all, if it just removes all entities anyway and wait for the response of my api-request ?



from Paging 3 library is not requesting my API after first usage

No comments:

Post a Comment