how can I refresh the page number in paging 3 library
Asked Answered
O

1

5

I'm using paging 3 to load some data in recycler view. I fetch data from the server and then store them in my local database. so here is my DAO interface:

@Dao
interface TicketDAO {


    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insert(model: TicketListModel)


    @Query("SELECT * FROM ticket_list ORDER BY lastModifyDate DESC , lastModifyTime DESC")
    fun getTickets(): PagingSource<Int, TicketListModel>


    @Query("delete from ticket_list")
    fun deleteAll()


}

I assumed this interface as my data source class. since I fetch data from the server too, therefore I used a remote mediator like this(by the way, I use volley for getting data from the server):


@ExperimentalPagingApi
class TicketRemoteMediator(val ticketDatabase: TicketDAO, val context: Context) :
    RemoteMediator<Int, TicketListModel>() {

    private val scope = CoroutineScope(Dispatchers.Default)
    private var page = 0
    private var reachedEnd = false
    override suspend fun load(
        loadType: LoadType,
        state: PagingState<Int, TicketListModel>
    ): MediatorResult {
        return try {
            fetchData(page = page)
            page += 1
            Log.i("Log","page is $page")
            MediatorResult.Success(endOfPaginationReached = reachedEnd)
        } catch (e: Exception) {
            page -= 1
            MediatorResult.Error(e)
        }

    }

    private fun fetchData(page: Int) {
        val request = object : JsonObjectRequest(
            Method.GET, Address().getTicketAPI(page), null, Response.Listener {
                reachedEnd = true
                val info = it.getJSONArray("results")
                for (i in 0 until info.length()) {
                    val data = info.getJSONObject(i)
                    scope.launch {
                        async(Dispatchers.Default, CoroutineStart.DEFAULT, block = {
                            ticketDatabase.insert(
                                TicketListModel(
                                    subject = data.getString("subject"),
                                    status = data.getString("status"),
                            )
                            reachedEnd = false
                        }).await()
                    }
                }

            }, Response.ErrorListener {
                this.page -= 1
                reachedEnd = false
                try {
                    Log.i("Log", "error in ticket remoteMediator ${String(it.networkResponse.data,Charsets.UTF_8)}")
                }catch (e:Exception){
                    Log.i("Log", "error in ticket mediator $it")
                }

            }
        ) {
            @Throws(AuthFailureError::class)
            override fun getHeaders(): MutableMap<String, String> {
                val token = HashMap<String, String>()
                token["Authorization"] =
                    "Bearer ${Config().accessToken}"
                return token
            }
        }
        request.retryPolicy = DefaultRetryPolicy(10000, 5, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT)
        val queue = Volley.newRequestQueue(context)
        queue.add(request)
    }
}

and use the data using my view model class like this:

    val isLoading = MutableLiveData<Boolean>()

    @ExperimentalPagingApi
    val tickets= Pager(
        config = PagingConfig(pageSize =5),
        remoteMediator = TicketRemoteMediator(ticketDatabase = database, context = context)
    ) {
        database.getTickets()

    }.liveData.cachedIn(viewModelScope).also {
        isLoading.value = false
    }

and I've got this chunk of code in my fragment:

            viewModel.tickets.observe(viewLifecycleOwner, Observer {
                viewLifecycleOwner.lifecycleScope.launch {
                    adapter.submitData(it)
                }
            })

which part of recycler view adapter is :


class TicketListAdapter :  PagingDataAdapter<TicketListModel, TicketListAdapter.ViewHolder>(
   diffCallback =      object : DiffUtil.ItemCallback<TicketListModel>() {
       override fun areItemsTheSame(oldItem: TicketListModel, newItem: TicketListModel): Boolean {
           return oldItem.ticket_id == newItem.ticket_id
       }
       override fun areContentsTheSame(
           oldItem: TicketListModel,
           newItem: TicketListModel
       ): Boolean {
           return oldItem == newItem
       }

   }
) {

...
}

now, I have two problems:

firstly: there's a time that my user wants to refresh the list. using swipe refreshing. so I set this line of code for doing that but nothing happens:

        binding.refresh.setOnRefreshListener {
            database.getTickets().invalidate()
        }

secondly: the paging library request from the server a lot even though the user has not scrolled yet. in fact, when I logged in, I saw that more than 20 requests are sent to the server while after 1 or 2 requests all of the information could be fetched from the server.

how can I solve these problems?

Outplay answered 3/11, 2020 at 12:9 Comment(0)
M
15

Refreshing is easy, you just need to call refresh() on your adapter

binding.refresh.setOnRefreshListener{
    pagingAdapter.refresh()
}

As for there being multiple requests, this is probably down to the pageSize you have given to PagingConfig. If you look at the other params in there you can see that the default value of initialLoadSize is three times pageSize. This means that it will attempt to load pageSize*3 items before it's happy. How many items are returned from Address().getTicketAPI(page)

Update:

I've just noticed that your load method always assumes we're trying to load the next page when it's actually called for refreshing and loading previous pages as well. It's the loadType param that dictates which direction we're trying to load.

Here's an example of what your load method should look like

val nextPage = when (loadType) {
    LoadType.REFRESH -> 0 // we're refreshing so just reset the page
    LoadType.PREPEND -> return MediatorResult.Success(endOfPaginationReached = true) // shouldn't need to prepend
    LoadType.APPEND -> {
        // work out the next page number
        page + 1
    }

return try {
     fetchData(page = nextPage)
     Log.i("Log","page is $nextPage")
     MediatorResult.Success(endOfPaginationReached = reachedEnd)
} catch (e: Exception) {
     MediatorResult.Error(e)
}
Manutius answered 4/11, 2020 at 9:25 Comment(4)
thanks, the refresh problem is solved, but still, the multi request is a problem. in fact, it keeps generating request as far as the endOfPaginationReached become true, it is not the main goal, I want to request for next page just when the user reached to end of the scroll viewOutplay
@Outplay I've updated my answer with a potential solution, let me know if you need a further explanationManutius
@Outplay I believe this issue is related to your layouts. This can also happen if you are using nestedScrollView and can also happen if using you haven't mentioned bottom constraint of your recyclerView.Expurgate
it will refresh the whole list, how to refresh/reload a specific page?Sickly

© 2022 - 2024 — McMap. All rights reserved.