'Android Paging 3: LoadType.APPEND returns null remote keys

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



Solution 1:[1]

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)
}

Solution 2:[2]

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)
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Jim Ovejera
Solution 2 Md. Asaduzzaman