I am trying to implement an android paging library with custom PageKeyedDataSource, This data source will query the data from Database and insert Ads randomly on that page.
I implemented the paging, but whenever I scroll past the second page and invalidate the data source, the recycler view jumps back to the end of the second page.
What is the reason for this?
DataSource:
class ColorsDataSource(
private val colorsRepository: ColorsRepository
) : PageKeyedDataSource<Int, ColorEntity>() {
override fun loadInitial(
params: LoadInitialParams<Int>,
callback: LoadInitialCallback<Int, ColorEntity>
) {
Timber.i("loadInitial() offset 0 params.requestedLoadSize $params.requestedLoadSize")
val resultFromDB = colorsRepository.getColors(0, params.requestedLoadSize)
// TODO insert Ads here
callback.onResult(resultFromDB, null, 1)
}
override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, ColorEntity>) {
val offset = params.key * params.requestedLoadSize
Timber.i("loadAfter() offset $offset params.requestedLoadSize $params.requestedLoadSize")
val resultFromDB = colorsRepository.getColors(
offset,
params.requestedLoadSize
)
// TODO insert Ads here
callback.onResult(resultFromDB, params.key + 1)
}
override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, ColorEntity>) {
// No- Op
}
}
BoundaryCallback
class ColorsBoundaryCallback(
private val colorsRepository: ColorsRepository,
ioExecutor: Executor,
private val invalidate: () -> Unit
) : PagedList.BoundaryCallback<ColorEntity>() {
private val helper = PagingRequestHelper(ioExecutor)
/**
* Database returned 0 items. We should query the backend for more items.
*/
@MainThread
override fun onZeroItemsLoaded() {
helper.runIfNotRunning(PagingRequestHelper.RequestType.INITIAL) { pagingRequestHelperCallback ->
Timber.i("onZeroItemsLoaded() ")
colorsRepository.colorsApiService.getColorsByCall(
ColorsRepository.getQueryParams(
1,
ColorViewModel.PAGE_SIZE
)
).enqueue(object : Callback<List<ColorsModel?>?> {
override fun onFailure(call: Call<List<ColorsModel?>?>, t: Throwable) {
handleFailure(t, pagingRequestHelperCallback)
}
override fun onResponse(
call: Call<List<ColorsModel?>?>,
response: Response<List<ColorsModel?>?>
) {
handleSuccess(response, pagingRequestHelperCallback)
}
})
}
}
private fun handleSuccess(
response: Response<List<ColorsModel?>?>,
pagingRequestHelperCallback: PagingRequestHelper.Request.Callback
) {
colorsRepository.saveColorsIntoDb(response.body())
invalidate.invoke()
Timber.i("onZeroItemsLoaded() with listOfColors")
pagingRequestHelperCallback.recordSuccess()
}
/**
* User reached to the end of the list.
*/
@MainThread
override fun onItemAtEndLoaded(itemAtEnd: ColorEntity) {
Timber.i("onItemAtEndLoaded() ")
helper.runIfNotRunning(PagingRequestHelper.RequestType.AFTER) { pagingRequestHelperCallback ->
val nextPage = itemAtEnd.nextPage?.toInt() ?: 0
colorsRepository.colorsApiService.getColorsByCall(
ColorsRepository.getQueryParams(
nextPage,
ColorViewModel.PAGE_SIZE
)
).enqueue(object : Callback<List<ColorsModel?>?> {
override fun onFailure(call: Call<List<ColorsModel?>?>, t: Throwable) {
handleFailure(t, pagingRequestHelperCallback)
}
override fun onResponse(
call: Call<List<ColorsModel?>?>,
response: Response<List<ColorsModel?>?>
) {
handleSuccess(response, pagingRequestHelperCallback)
}
})
}
}
private fun handleFailure(
t: Throwable,
pagingRequestHelperCallback: PagingRequestHelper.Request.Callback
) {
Timber.e(t)
pagingRequestHelperCallback.recordFailure(t)
}
}
Adapter's DiffUtil
class DiffUtilCallBack : DiffUtil.ItemCallback<ColorEntity>() {
override fun areItemsTheSame(oldItem: ColorEntity, newItem: ColorEntity): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: ColorEntity, newItem: ColorEntity): Boolean {
return oldItem.hexString == newItem.hexString
&& oldItem.name == newItem.name
&& oldItem.colorId == newItem.colorId
}
}
ViewModel
class ColorViewModel(private val repository: ColorsRepository) : ViewModel() {
fun getColors(): LiveData<PagedList<ColorEntity>> = postsLiveData
private var postsLiveData: LiveData<PagedList<ColorEntity>>
lateinit var dataSourceFactory: DataSource.Factory<Int, ColorEntity>
lateinit var dataSource: ColorsDataSource
init {
val config = PagedList.Config.Builder()
.setPageSize(PAGE_SIZE)
.setEnablePlaceholders(false)
.build()
val builder = initializedPagedListBuilder(config)
val contentBoundaryCallBack =
ColorsBoundaryCallback(repository, Executors.newSingleThreadExecutor()) {
invalidate()
}
builder.setBoundaryCallback(contentBoundaryCallBack)
postsLiveData = builder.build()
}
private fun initializedPagedListBuilder(config: PagedList.Config):
LivePagedListBuilder<Int, ColorEntity> {
dataSourceFactory = object : DataSource.Factory<Int, ColorEntity>() {
override fun create(): DataSource<Int, ColorEntity> {
dataSource = ColorsDataSource(repository)
return dataSource
}
}
return LivePagedListBuilder<Int, ColorEntity>(dataSourceFactory, config)
}
private fun invalidate() {
dataSource.invalidate()
}
companion object {
const val PAGE_SIZE = 8
}
}