Search code examples
androidmvvmrx-javaandroid-livedataandroid-paging-library

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


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


Solution

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