Search code examples
androidandroid-jetpackandroidxandroid-architecture-componentsandroid-paging-library

Paging library data source with specific page index


Regarding the paging library in the arch. components

It has three different datasources:

  1. PageKeyedDataSource: When a request requires next/previous index keys.
  2. ItemKeyedDataSource: When a request requires an item as a key.
  3. PositionalDataSource: When a request requires an index to fetch next batch.

Which one to use if the backend response doesn't support next/previous index keys but only current page. when the response is as follow :

{
    "status": "success",
    "response": "1",
    "message": "Data fetched successfully",
    "data": {
        "total": 2,
        "per_page": "5",
        "page": 1,
        "items": [],
        "last_page": 1,
        "has_more": false
    }
}

Solution

  • This looks a lot like the StackOverflow response I've used in this sample.

    {
      "items": [
      ],
      "has_more": true,
      "quota_max": 10000,
      "quota_remaining": 9965
    }
    

    The retrofit interface I used looks like this:

    @Headers("Content-Type: application/json", "Accept: application/json")
        @GET("/2.2/users")
        fun getTopUsers(
                @Query("page") page: Int,
                @Query("pagesize") pagesize: Int,
                @Query("order") order: String = "desc",
                @Query("sort") sort: String = "reputation",
                @Query("site") site: String = "stackoverflow"
    
        ): Single<SOResponse<User>>
    

    So we can specify the page size and the number of the page, like in your case.

    For this I used a PageKeyedDataSource<Int, User>. Where you specify the page you want to load by passing an integer. For example, my load initial method looks like this:

    override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, User>) {
            loadState.onNext(NetworkState.LOADING)
            val disposable = soApi.apiService.getTopUsers(1, params.requestedLoadSize)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe({
                        callback.onResult(it.items, 1, 2)
                        loadState.onNext(NetworkState.LOADED)
                    }, {
                        loadState.onNext(NetworkState.error(it.message))
                    })
    
            disposables.add(disposable)
        }
    

    If you check the callback.onResult(it.items, 1, 2) you'll see that I'm requesting the next page, which is 2.

    For the other pages, I'm using this:

    override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, User>) {
            loadState.onNext(NetworkState.LOADING)
            val disposable = soApi.apiService.getTopUsers(params.key, params.requestedLoadSize)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe({
                        callback.onResult(it.items, params.key + 1)
                        loadState.onNext(NetworkState.LOADED)
                    }, {
                        loadState.onNext(NetworkState.error(it.message))
                    })
            disposables.add(disposable)
        }
    

    This time I'm just incrementing the page number callback.onResult(it.items, params.key + 1)