Android Paging 3 - get list of data from PagingData<T> object
Asked Answered
U

3

40

I'm using new jetpack Paging 3 library. I have one specific use case. I want to share this data between two screens using viewmodel.

One screen needs paged data and for the second screen I want simple list of this data (not paged list, I need to work with the list...). I do not want list in object of PagingData.

Is there any way how to get the list without PagingData object ? enter image description here

As you can see there is no variable the get this data. I tried it even in viewModel when flow is created but I haven't found solution.

Unused answered 2/12, 2020 at 16:44 Comment(0)
A
20

PagingData is just a stateless stream of incremental load events, so it does not hold this kind of state.

However PagingDataAdapter / Differ (and similarly other presenter-side variants), already need to hold the data, so they expose APIs such as PagingDataAdapter.snapshot() which can give you the current list of presented items.

Keep in mind that the presented data is post-transformation, may not include pages that were dropped due to exceeding maxSize, and also may race with fetcher as it takes some time between loading the data, and having it show up in UI.

It really depends on what you're interested in tracking exactly, but from the fetcher side (tracking pre-transform) you can either build the list from results returned in PagingSource or you can also write a no-op .map() operator on PagingData which will see every item (but not give you information about order).

Unfortunately Paging3 doesn't yet offer a non-ui collector that can build this state for you (most useful in testing), but this is hopefully something we can look into in the future.

Arterial answered 3/12, 2020 at 1:15 Comment(3)
Currently the only way is to collect from it using a presenter api like the differ and take snapshots after its loaded. Note this doesn't mean UI test, just android test. I would also heavily suggest testing components separately (PagingSource is directly callable, etc)Arterial
@Arterial For my use case, I was interested in having data in a sealed view state object.Strasbourg
There currently is no public api way of collecting internal PageEvents from PagingData directly, so you cannot re-create the state that the presenter APIs track without reflection. Also, I would expect it to change in future releases, so I wouldn't even try it - although we are looking into something more independent for testability and wanting to open up the API more. It just requires time because once we open it up, we can't take it back since people will depend on its stability, so we must make sure we design it correctly.Arterial
G
4

To expand on dlam's comment, it is possible to retrieve the list of data of PagingData, as long as it is connected to a PagingDataAdapter, or if the PagingData result is submitted to a AsyncPagingDataDiffer.

To do that, invoke

adapter.snapshot().items

if connected to a PagingDataAdapter, or

differ.snapshot().items

if connected to a AsyncPagingDataDiffer.

Grilse answered 1/3, 2022 at 11:40 Comment(1)
register a RecyclerView.AdapterDataObserver to your adapter and use adapter.snapshot().getItems(); to get a List of items every time its updated.Ailey
P
2

There is no direct way to get list of data from PagingData object. If you are not using/or don't want to use PagingDataAdapter, you can use AsyncPagingDataDiffer, which is quick and easy to implement. If you are planning to write unit test for paging then also it is useful.

Create an instance of AsyncPagingDataDiffer

val differ = AsyncPagingDataDiffer(
        diffCallback = TestDiffCallback<RecentChatUi>(),
        updateCallback = TestListCallback(),
        workerDispatcher = Dispatchers.Main
    )

you have to pass one DiffUtil.ItemCallback and ListUpdateCallback

class TestListCallback : ListUpdateCallback {
    override fun onChanged(position: Int, count: Int, payload: Any?) {}
    override fun onMoved(fromPosition: Int, toPosition: Int) {}
    override fun onInserted(position: Int, count: Int) {}
    override fun onRemoved(position: Int, count: Int) {}
}

class TestDiffCallback<T> : DiffUtil.ItemCallback<T>() {
    override fun areItemsTheSame(oldItem: T, newItem: T): Boolean {
        return oldItem == newItem
    }

    override fun areContentsTheSame(oldItem: T, newItem: T): Boolean {
        return oldItem == newItem
    }
}

then submit the paging data to the adapter and get the list

differ.submitData(pagerData)
val list = differ.snapshot().items
Podagra answered 9/2, 2023 at 16:27 Comment(1)
I have to mention snapshot() call would not give you dataset at least in case if you haven't submitted yet pagerData into adapter. I had such use case.Bette

© 2022 - 2024 — McMap. All rights reserved.