I am trying to cache list from api response and sync the data with the server, I followed up with Codelab to create the single source of truth as android documentation shows up using paging 3, I followed the steps and i was surprised with the results, but when i tried to cache the paging data i'v got this thrown error while just the first ever running and continue work when the app crashes or while the first running without internet connection
InvalidObjectException("Remote key and the prevKey should not be null")
and it seams came from :
private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, MohItem>): RemoteKeys? {
return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
?.let { mohItem ->
database.remoteKeysDao().remoteKeysId(mohItem.id)
}
}
It's convenience for me that it's since it's trying to insert keys which isn't received from empty or null list as the snipped code shows up :
@ExperimentalPagingApi
class MohRemoteMediator(
private val context: Context,
private val database: IbnsinaDatabase,
private val apiService: ApiService,
private val query: MohSearchQueryRequest
) : RemoteMediator<Int, MohItem>() {
override suspend fun load(
loadType: LoadType,
state: PagingState<Int, MohItem>
): MediatorResult {
val page = when (loadType) {
LoadType.REFRESH -> {
val remoteKeys = getRemoteKeyClosestToCurrentPosition(state)
remoteKeys?.nextKey?.minus(1) ?: Constants.PAGING_STARTING_PAGE_INDEX
}
LoadType.PREPEND -> {
val remoteKeys = getRemoteKeyForFirstItem(state)
?: throw InvalidObjectException("Remote key and the prevKey should not be null")
remoteKeys.prevKey ?: return MediatorResult.Success(endOfPaginationReached = true)
remoteKeys.prevKey
}
LoadType.APPEND -> {
val remoteKeys = getRemoteKeyForLastItem(state)
if (remoteKeys?.nextKey == null) {
throw InvalidObjectException("Remote key should not be null for $loadType")
}
remoteKeys.nextKey
}
}
try {
val apiResponse = apiService.getMohList(
pageIndex = page,
title = query.title,
number = query.number,
month = query.month,
year = query.year
)
val mohs = apiResponse.data ?: emptyList()
val endOfPaginationReached = mohs.isEmpty()
database.withTransaction {
if (loadType == LoadType.REFRESH) {
database.remoteKeysDao().clearRemoteKeys()
database.mohDao().clearMohs()
}
val prevKey = if (page == Constants.PAGING_STARTING_PAGE_INDEX) null else page - 1
val nextKey = if (endOfPaginationReached) null else page + 1
val keys = mohs.map {
RemoteKeys(mohId = it.id, prevKey = prevKey, nextKey = nextKey)
}
database.remoteKeysDao().insertAll(keys)
database.mohDao().insertAllMohs(mohs)
}
return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached)
} catch (exception: IOException) {
return MediatorResult.Error(Throwable(context.getString(R.string.no_internet_connection)))
} catch (exception: HttpException) {
return MediatorResult.Error(exception)
}
}
private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, MohItem>): RemoteKeys? {
return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
?.let { repo ->
// Get the remote keys of the first items retrieved
database.remoteKeysDao().remoteKeysId(repo.id)
}
}
private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, MohItem>): RemoteKeys? {
return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
?.let { mohItem ->
database.remoteKeysDao().remoteKeysId(mohItem.id)
}
}
private suspend fun getRemoteKeyClosestToCurrentPosition(
state: PagingState<Int, MohItem>
): RemoteKeys? {
return state.anchorPosition?.let { position ->
state.closestItemToPosition(position)?.id?.let { repoId ->
database.remoteKeysDao().remoteKeysId(repoId)
}
}
}
}