Android Paging 3: LoadType.APPEND returns null remote keys

Viewed 837

I've been trying to work out how to fix my issue with RemoteMediator's APPEND LoadType.

On an empty Room DB, here's how the LoadType flows: REFRESH -> PREPEND -> APPEND (remoteKeys = null, endOfPaginationReached = true)

With at least 10 rows for entity and remote keys table, here's how the LoadType flows: REFRESH -> PREPEND -> APPEND (remoteKeys = prev=null, next=2, endOfPaginationReached = false)

Clearly, my issue is on a fresh installed device (with empty Room DB), the user won't see more than 10 items because APPEND's state.lastItemOrNull() is returning null.

Here's my code so far:

private suspend fun getRemoteKeysForLastItem(state: PagingState<Int, MovieCache>): MovieRemoteKeys? {
    return state.lastItemOrNull()?.let { movie ->
        appDatabase.withTransaction {
            appDatabase.remoteKeysDao().remoteKeysByImdbId(movie.imdbId)
        }
    }
}

for my load() function:

val loadKey = when (loadType) {
            LoadType.REFRESH -> {
                val key = getRemoteKeysClosestToCurrentPosition(state)
                Timber.d("REFRESH key: $key, output: ${key?.nextKey?.minus(1)}")
                key?.nextKey?.minus(1) ?: 1
            }
            LoadType.PREPEND -> {
                Timber.d("PREPEND key requested")
                return MediatorResult.Success(true)
            }
            LoadType.APPEND -> {
                val key = getRemoteKeysForLastItem(state)
                Timber.d("APPEND key: $key")
                appDatabase.withTransaction {
                    val size = movieDao.movies().size
                    val remoteSize = remoteKeysDao.allKeys().size
                    Timber.d("APPEND DB size: $size, remote: $remoteSize")
                }
                key?.nextKey ?: return MediatorResult.Success(true)
            }
        }

Here's a sample logcat showing that APPEND is null enter image description here

Leaving my app unable to scroll down even at least once!

Edit: Here is how I save my remote keys: enter image description here

2 Answers

Finally, this is how I managed to fix this issue, by relying on the remoteKeys on the DB than PagingState:

LoadType.APPEND -> {
//  val key = getRemoteKeysForLastItem(state) // Doesn't work. state returns NULL even Room has data for both remote keys and entity.
    val key = appDatabase.withTransaction {
        remoteKeysDao.allKeys().lastOrNull() // Workaround
    }
    key?.nextKey ?: return MediatorResult.Success(true)
}

Instead of returning endOfPaginationReached = true from LoadType.APPEND when remote key is null, return endOfPaginationReached = key != null. Returning endOfPaginationReached = false means keep loading which will give you the remote key next time.

LoadType.APPEND -> {
    val key = getRemoteKeysForLastItem(state)
    Timber.d("APPEND key: $key")
    appDatabase.withTransaction {
        val size = movieDao.movies().size
        val remoteSize = remoteKeysDao.allKeys().size
        Timber.d("APPEND DB size: $size, remote: $remoteSize")
    }
    key?.nextKey ?: return MediatorResult.Success(key != null)
}
Related