Updating Paging 3 Alpha to Stable Cause Indexing Issue Android

Calling refresh method on paging data in jetpack compose not working correctly

I resolve this issues by returning null in the getRefreshKey() method

How can I correctly use custom PagingSource with PagingDataAdapter, on local data?

UPDATE July 2022

There are some problems with paging-3. The most noticeable ones are

  • failure to properly load the dataset when using an initial position with initialKey (see initial_key branch)
  • failure to properly jump to another position with scrollToPositionWithOffset or similar function (see test_scroll branch)

getRefreshKey function is called with weird (sometimes negative) values of anchorPosition, and load is called multiple times to load unnecessary data - sometimes loading iteratively every page from the end or from the beginning.

I believe the same problem can be observed when the PagingSource is generated by Room.

=> In short, it can only be used if you need to display the top items first, and if you don't need to jump to a particular position (seeing other items is only done when the user manually scrolls through the list). A fix is on the way but it could take time; it's still only alpha and compiles for a specific set of library versions / SDK.

You'll find another, simpler algorithm for getRefreshKey and load in the mentioned branches, in CheeseDao.kt.

End of update


I finally found out a possible solution, though I'm not sure it will work if the Paging-3 library is updated. I don't know yet if the behaviour explained above is due to bugs / limitations in the Paging-3 components or if that's just a bad explanation in the reference documentation, or something I missed entirely.

I. Work-around for the glitch issue.

  1. In PagingConfig, we must have enablePlaceholders = true or it simply won't work correctly. With false I'm observing the scrollbar jumping on each load operation when scrolling up/down, and inserting items at the end will make all items glitch on the display, then the list will jump all the way to the top.

  2. The logic in getRefreshKey and load, as shown in Google's guide and reference documentation, is naive and will not work with custom data. I had to modify them as follows (modifications have been pushed in the github example):

        override fun getRefreshKey(state: PagingState<Int, Cheese>): Int? = state.anchorPosition

override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Cheese> {
data class Args(
var start: Int = 0, var size: Int = 0, var prevKey: Int? = null, var nextKey: Int? = null,
var itemsBefore: Int = UNDEF, var itemsAfter: Int = UNDEF
)
val pos = params.key ?: 0
val args = Args()
when (params) {
is LoadParams.Append -> {
args.start = pos
args.prevKey = params.key
//args.nextKey = if (args.start < count) min(args.start + params.loadSize, count) else null
args.nextKey = if (args.start + params.loadSize < count) args.start + params.loadSize else null
}
is LoadParams.Prepend -> {
args.start = max(pos - pageSize, 0)
args.prevKey = if (args.start > 0) args.start else null
args.nextKey = params.key
}
is LoadParams.Refresh -> {
args.start = max((pos - params.loadSize/2)/pageSize*pageSize, 0)
args.prevKey = if (args.start > 0) args.start else null
args.nextKey = if (args.start + params.loadSize < count) min(args.start + params.loadSize, count - 1) else null
}
}
args.size = min(params.loadSize, count - args.start)
if (params is LoadParams.Refresh) {
args.itemsBefore = args.start
args.itemsAfter = count - args.size - args.start
}
val source = dao.allCheesesOrdName()
val data = source.drop(args.start).take(args.size)
if (params.key == null && data.count() == 0) {
return LoadResult.Error(Exception("Empty"))
}
val result = LoadResult.Page(
data = data,
prevKey = args.prevKey,
nextKey = args.nextKey,
itemsBefore = args.itemsBefore,
itemsAfter = args.itemsAfter
)
return result
}

I had to deduce that from the behaviour of Room-generated PagingSource, by inserting a wrapper between the DAO code and the view model to observe the params and LoadResult values.

Notes

  • The commented line in LoadParam.Append mimics the Room-generated code behaviour, which is slightly incorrect when loading the last page. It makes the Pager loads an empty page at the end, which is not a serious issue but triggers unnecessary operations in the whole chain.
  • I'm not sure about the Refresh case, it was hard to induce any logic from Room's code behaviour. Here I'm placing the params.key position in the middle of the loaded range (params.loadSize items). At worst it will append data in a 2nd load operation.

II. Predicting the discarded data

This should be possible from the params given to the load function. In a simplified heuristic (the ranges must be min/max'ed to get actual indices):

LoadParams.Append  -> loads [key .. key+loadSize[, so discard [key-maxSize..key-maxSize+loadSize[
LoadParams.Prepend -> loads [key-loadSize .. key[, do discard [key-loadSize+maxSize..key+maxSize[
LoadParams.Refresh -> discard what is not reloaded

The Args.start .. Args.start + Args.size in the code above can be used as the range which is kept, from there it is easy to deduce what is discarded.

Could not get unknown property 'implementation' for object of type Dependency

I have added it like this and it works:

implementation 'androidx.paging:paging-runtime-ktx:3.1.0'


Related Topics



Leave a reply



Submit