Api call is not stopped calling again and again without any scrolling in Google Paging Library 3

Viewed 1250

I want to implement pagination to show a chunk of list of my desire item view in my app. That's why I choose to use Google newly released paging library i.e Paging library 3. I use Rxjava, Livedata, and ViewModel in my app. After implementing the paging library, I am facing a weird problem. When I call the method for fetching list, it's calling again and again and not stopped calling the call. In fact, it automatically increases the page number although I did not scroll the list. Here is the code I tried

JobListRestApi.kt

interface JobListRestApi {
    @GET("job/list")
    fun getJobResponse(
        @Query("page") pageNumber: Int
    ): Single<Response<JobResponse>>
}

JobListDataSource.kt

class JobListDataSource @Inject constructor(
    private val jobListRestApi: JobListRestApi
): RxPagingSource<Int, Job>() {

    override fun loadSingle(params: LoadParams<Int>): Single<LoadResult<Int, Job>> {
        val position = params.key ?: 1

        return jobListRestApi.getJobResponse(position).toSingle()
            .subscribeOn(Schedulers.io())
            .map { jobResponse -> jobResponse.jobData.jobs }
            .map { jobs -> toLoadResult(jobs, position) }
            .onErrorReturn { LoadResult.Error(it) }
    }

    private fun toLoadResult(data: ArrayList<Job>, position: Int): LoadResult<Int, Job> {
        val prevKey = if (position == 1) null else position-1
        val nextKey = if (position == data.size) null else position+1

        return LoadResult.Page(data, prevKey, nextKey)
    }

}

JobListRepositoryImpl.kt

class JobListRepositoryImpl @Inject constructor(
    private val jobListDataSource: JobListDataSource
): JobListRepository {

    override fun getJobs(): Flowable<PagingData<Job>> {
        return Pager(PagingConfig(pageSize = 20)) {
            jobListDataSource
        }.flowable
    }
}

JobListViewModel.kt

class JobListViewModel @Inject constructor(
    private val jobListRepository: JobListRepository
): BaseViewModel() {

    val jobs: MutableLiveData<PagingData<Job>> = MutableLiveData()

    fun getJobs() {
        if (jobs.value == null) {
            compositeDisposable += jobListRepository.getJobs()
                .subscribe({
                    jobs.value = it
                }, {
                    handleException(it)
                })
        }
    }

}

JobListFragment.kt

class JobListFragment : BaseFragment<JobListViewModel>() {

    private val jobAdapter: JobAdapter by lazy {
        JobAdapter { }
    }

    override fun getLayoutResource() = R.layout.fragment_job_list

    override fun initWidget() {
        job_recycler_view.adapter = jobAdapter
    }

    override fun onResume() {
        super.onResume()
        viewModel.getJobs()
    }

    override fun observeLiveData() {
        observe(viewModel.jobs) {
            jobAdapter.submitData(lifecycle, it)
        }
    }
}

And the output log is https://base-url/job/list?page=1

https://base-url/job/list?page=2

https://base-url/job/list?page=3

https://base-url/job/list?page=4

https://base-url/job/list?page=5

https://base-url/job/list?page=6

https://base-url/job/list?page=7

how can I stop calling serial api unless I go to the last item of the chunk in RecyclerView and scroll the list

2 Answers

You tell Paging there is no more to load by returning null for prevKey and nextKey inLoadResult.Page

Since its infinitely appending, it looks like you never set nextKey to null. Perhaps you meant to check data.isEmpty() instead of key == data.size?

Eventually, I got the error. In fact, the problem was in my Http response Data. The updated DataSource is given bellow

JobListDataSource.kt

class JobListDataSource @Inject constructor(
    private val jobListRestApi: JobListRestApi
): RxPagingSource<Int, Job>() {

    override fun loadSingle(params: LoadParams<Int>): Single<LoadResult<Int, Job>> {
        val position = params.key ?: 1

        return jobListRestApi.getJobResponse(position).toSingle()
            .subscribeOn(Schedulers.io())
            .map { jobResponse -> jobResponse.jobData }
            .map { jobData -> toLoadResult(jobData, position) }
            .onErrorReturn { LoadResult.Error(it) }
    }

    private fun toLoadResult(data: JobData, position: Int): LoadResult<Int, Job> {
        val prevKey = if (position == 1) null else position-1
        val nextKey = if (data.hasMore) position+1 else null

        return LoadResult.Page(data.jobs, prevKey, nextKey)
    }

}
Related