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