I try to implement Paging 3 with Rx in my sample Android project. Data is fetched from remote, and comes to PagingSource. But it seems, that it doesn't proceed further. When debugging ViewModel and Activity related parts, PagingData is empty. Here is the code:
PagingSource:
package com.android.photosearch.data.source.remote
import androidx.paging.PagingSource.LoadResult.Page.Companion.COUNT_UNDEFINED
import androidx.paging.PagingState
import androidx.paging.rxjava2.RxPagingSource
import com.android.photosearch.data.source.remote.model.PhotoSearchResponse
import com.android.photosearch.domain.model.Photo
import com.android.photosearch.util.Constants
import io.reactivex.Single
import io.reactivex.schedulers.Schedulers
class PhotoPagingSource(
private val retrofitService: RetrofitService,
private var query: String
) : RxPagingSource<Int, Photo>() {
override fun loadSingle(
params: LoadParams<Int>
): Single<LoadResult<Int, Photo>> {
// Start refresh at page 1 if undefined.
var nextPageNumber = params.key
if (nextPageNumber == null) {
nextPageNumber = 1
}
return retrofitService.getPhotos(query, Constants.PHOTO_PAGE_SIZE, nextPageNumber)
.subscribeOn(Schedulers.io())
.map { response: PhotoSearchResponse -> toLoadResult(response, nextPageNumber) }
.onErrorReturn { LoadResult.Error(it) }
}
private fun toLoadResult(
response: PhotoSearchResponse,
position:Int
): LoadResult<Int, Photo> {
return LoadResult.Page(
response.photosGeneralResponse.photoList,
null, // Only paging forward.
position + 1,
COUNT_UNDEFINED,
COUNT_UNDEFINED
)
}
override fun getRefreshKey(state: PagingState<Int, Photo>): Int? {
// Try to find the page key of the closest page to anchorPosition, from
// either the prevKey or the nextKey, but you need to handle nullability
// here:
// * prevKey == null -> anchorPage is the first page.
// * nextKey == null -> anchorPage is the last page.
// * both prevKey and nextKey null -> anchorPage is the initial page, so
// just return null.
val anchorPosition = state.anchorPosition ?: return null
val (_, prevKey, nextKey) = state.closestPageToPosition(anchorPosition)
?: return null
if (prevKey != null) {
return prevKey + 1
}
return if (nextKey != null) {
nextKey - 1
} else null
}
}
Repository:
package com.android.photosearch.data.repository
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.rxjava2.flowable
import com.android.photosearch.data.source.remote.PhotoPagingSource
import com.android.photosearch.data.source.remote.RetrofitService
import com.android.photosearch.domain.model.Photo
import com.android.photosearch.domain.repository.PhotoRepository
import io.reactivex.Flowable
import kotlinx.coroutines.ExperimentalCoroutinesApi
import javax.inject.Inject
/**
* This repository is responsible for
* fetching data [photo] from server
* */
class PhotoRepositoryImp @Inject constructor(
private val retrofitService: RetrofitService
) : PhotoRepository {
@ExperimentalCoroutinesApi
override fun getPhotos(
searchQuery: String,
photosPerPage: Int
): Flowable<PagingData<Photo>> {
val pagingSource = PhotoPagingSource(retrofitService, searchQuery)
return Pager(
config = PagingConfig(
pageSize = 20,
prefetchDistance = 1,
enablePlaceholders = false
),
pagingSourceFactory = { pagingSource }
).flowable
}
}
ViewModel:
...
fun loadPhotos() : Flowable<PagingData<Photo>> {
return repository.getPhotos(searchQuery, Constants.PHOTO_PAGE_SIZE)
}
...
And UI:
mDisposable.add(viewModel.loadPhotos().subscribe { pagingData ->
searchResultsAdapter?.submitData(lifecycle,pagingData)
})
Any help will be highly appreciated.
Update, adding LoadState listener info
CombinedLoadStates( refresh=Loading(endOfPaginationReached=false), prepend=NotLoading(endOfPaginationReached=true), append=NotLoading(endOfPaginationReached=false), source=LoadStates(refresh=Loading(endOfPaginationReached=false), prepend=NotLoading(endOfPaginationReached=true), append=NotLoading(endOfPaginationReached=false)), mediator=null )
Update 2
Thank You for You efforts, @dlam ! I have found the cause of issue. It was in my adapter. So stupid error.. First I wrote common RecyclerView.Adapter, and then I've changed it to PagingDataAdapter. But I've forgot to remove "getItemCount" method... ). Here is the code:
package com.android.photosearch.presentation.search
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.android.photosearch.R
import com.android.photosearch.databinding.HolderPhotoBinding
import com.android.photosearch.domain.model.Photo
import com.android.photosearch.presentation.search.SearchResultsAdapter.PhotoViewHolder
import java.util.*
/**
* This class is responsible for converting each data entry [Photo]
* into [PhotoViewHolder] that can then be added to the AdapterView.
*/
class SearchResultsAdapter(diffCallback: DiffUtil.ItemCallback<Photo>) :
PagingDataAdapter<Photo, PhotoViewHolder>(diffCallback) {
// This line should be removed too, as unnecessary.
private val photos: MutableList<Photo> = ArrayList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PhotoViewHolder {
val holderPhotoBinding = DataBindingUtil.inflate<ViewDataBinding>(
LayoutInflater.from(parent.context),
R.layout.holder_photo,
parent,
false
)
return PhotoViewHolder(holderPhotoBinding)
}
override fun onBindViewHolder(holder: PhotoViewHolder, position: Int) {
getItem(position)?.let { holder.onBind(it) }
}
// This method should be removed.
override fun getItemCount(): Int {
return photos.size
}
inner class PhotoViewHolder(private val dataBinding: ViewDataBinding) :
RecyclerView.ViewHolder(dataBinding.root) {
fun onBind(photo: Photo) {
val holderPhotoBinding = dataBinding as HolderPhotoBinding
val photoViewModel = PhotoViewModel(photo)
holderPhotoBinding.photoViewModel = photoViewModel
}
}
}
And the thing is that I've been able to debug and find this error, only after switching to Paging2. Because that "PagingData" of Paging3 was very hard to debug.