Android paging 3 reload current page

Pagination in Android with Paging 3, Retrofit and kotlin Flow

Haven’t you asked how does Facebook, Instagram, Twitter, Forbes, etc… let you “scroll infinitely” without reach a “end” of information in their apps? Wouldn’t you like to implement something like that?

This “endless scrolling” feature is commonly called “Pagination” and it’s nothing new. In a brief resume, pagination help you to load chunks of data associated with a “page” index.

Let’s assume you have 200 items to display in your RecyclerView. What you would do normally is just execute your request, get the response items and submit your list to your adapter. We already know that RecyclerView by it’s own is optimized for “recycle our views”. But do we really need to get those 200 items immediately? What if our user never reach the top 100, or top 50? The rest of the non displayed items are still keep on memory.

What pagination does (in conjunction with our API) is that it let us establish a page number, and how many items per page can we load. In that way, we can request for the next page of items only when we reach the bottom of our RecyclerView.

Non library approach

Before paging 3, you could have implemented Pagination by adding some listeners to your RecyclerView and those listeners would be triggered when you reach the bottom of your list. There are some good samples about it, here is a video with a vey detailed explanation (it’s on Indi, but it is understandable anyways).

A real PRODUCTION ready example is on the Plaid app. Look at their InfinteScrollListener class.

Android Jetpack Paging 3 and Flow

Today you gonna learn how to implement Pagination by using paging 3 from the android jetpack libraries. For my surprise the codelab of Paging 3 was one of the most easiest I have ever done. Florina Muntenescu did a great job with each step of the codelab, go check it out and give it a try. If you want to go straight to the sample code, check this pull request and see step by step how I implement paging 3 to this project.

Источник

Paging 3 Android Tutorial

When working with RecyclerViews in our Android project, we display a list with a lot of items in it. Sometimes we have use-case like fetching a contact list from the phone and displaying it on the list. Loading a lot of data at once in a list is not a very efficient task to do. To get over this, we have paging in Android.

By using the Paging library, we can load a lot of data efficiently, and only we will load the data when needed. In this blog, we will be learning about the Paging 3 library in Android and how we can use it to load our large set of data in our RecyclerView efficiently.

In this blog, we are going to learn,

  • Introduction to Paging 3 library
  • Understanding and Implementing Paging 3 library.
  • Getting the States of the data
  • Adding the Header and Footer view.
  • Using it with RxJava.

Introduction to Paging 3 library

Google launched Paging 3 as a part of the Jetpack series. It is still in the early stages. Paging 3 is written entirely using Kotlin Coroutines. It has support for requesting the next page to load more data automatically. It supports Flow, LiveData, and RxJava along with Kotlin Coroutine.

Paging 3 also adds support for caching and handles the state efficiently like loading and failed states while loading the items on ti the list. It also keeps track for the keys to get data from the next and previous pages.

Understanding and Implementing Paging 3 library

In this, we are going to learn implementing Paging 3 library by fetching the list from an API and displaying it in the list. We are going to use the following API for displaying the list,

Our project structure looks like,

Here, we have an adapter package which is responsible for displaying the list in the RecyclerView. data package is responsible for fetching data from the API. We also have MainActivity in which we are going to display the list.

So, now let’s get started.

Step 01.

Let’s first setup the dependencies for Paging 3 in our app’s build.gradle like,

Note: At the time of writing the blog, its version is 3.0.0-alpha04

and we will also add the support for Retrofit and Moshi as we are going to use Moshi as convertor factory for Retrofit like,

Читайте также:  Таймер для андроида интервальный

Step 02.

Now, we are going to setup APIService and Retrofit client like,

Here, we have created an APIService interface with getListData function which takes a query parameter which will take the page number from which we have to fetch the data from.

We also setup the Retrofit client, by adding the base URL and the convertor factory of Moshi.

Step 03.

Now, we have to create the data class for the following JSON output which we get as the response,

and we create the corresponding data classes for the above JSON response. Our ApiResponse data class looks like,

The Ad data class looks like,

and Data data class looks like,

Step 04.

Now, since we have setup the data class and APIService, let’s setup the paging library. In PostDataSource we will update the code as,

Here, we have extended PostDataSource with PagingSource which will implement a suspend load function which will help us to load the data.

PostDataSource also takes a primary constructor parameter APIService. PostDataSource acts here as a repository and the load function gets the data from the API.

Since the load function is a suspend function, we can call other suspend functions inside it without any issues which we created in APIService.

In the PostDataSource, we take two parameters one of integer type and other of the data type we have to load on the list item. The integer parameter represents the page number here.

Now, we update the load function like,

Here, we get the page number from params and assign it to nextPage variable using param.key and if it returns null, we set a default value 1.

We also do the API call and get assign the response to the response variable using APIService which we passed as a constructor parameter to PostDataSource class.

After doing all the operations with the successful response, we return the LoadResult.Page object here with the required data and it something went wrong we use LoadResult.Error.

We are also passing null as the next key if there is no corresponding data in that direction.

Here, the PostDataSource is responsible to keep track of the page which has to be requested next which we get from the APIService.

Step 05.

Now, since we have the DataSource setup we will now get the data in the MainViewModel. We will create the MainViewModel by adding the APIService as a parameter to the primary constructor. So, we will update the ViewModel like,

Now, inside the ViewModel, we will create a variable called listData and assign it to the Pager instance.

Here, the Pager will call the load function from the PostDataSource and we will pass the apiService as well in the object of PostDataSource.

In the Pager object, we also pass the configuration which is used to configure the behavior of Paging and how it should load its data. We are passing the PagingConfig with the pageSize.

PageSize specifics the number of items loaded at once from the PostDataSource. Recommended is to always keep the page size to more than the visible items on the screen.

And lastly, we will convert the listData as flow by adding .flow .

It will convert the stream of data into a Flow. If we want to return LiveData we can replace .flow with .liveData.

And at last, we cache the data in viewModelScope, and the data will be alive until the scope is active. Here, we are using viewModelScope and since we are caching the data in the ViewModel it would not be impacted on any configuration changes.

Step 06.

Now, since we are getting the data in ViewModel, we now need to pass the data in the RecyclerView of MainActivity. We will update the activity_main.xml like,

Now, we will update the MainActivity like,

Here, we have setup our ViewModel by passing the MainViewModelFactory with the instance of APIService.

The MainViewModelFactory looks like,

And then we have to setup the RecyclerView in the setupList function and lastly, in the setupView function, we will collect the data from listData variable from ViewModel inside the lifecycleScope and then pass it to the submitData function which is part of PagingDataAdapter which helps us to update the data we fetched from the API and display in the UI.

Step 07.

Now, we will update the MainListAdapter. We will extend the MainListAdapter with PagingDataAdapter and the PagingDataAdapter will take the type of data we need to display in the list and the ViewHolder.

It also takes a DiffUtil callback, as a parameter to its primary constructor which helps the PagingDataAdapter to update the items if they are changed or updated. And DiffUtil callback is used because they are more performant.

Now, the MainListAdapter looks like,

Here, in the MainListAdapter, inside the onCreateViewHolder we are returning the ViewHolder by inflating the R.layout.list_item.

Читайте также:  Блокирующий вирус с андроид

Here, the list_item layout file looks like,

and inside the onBindViewHolder we will assign the full name and email to both the TextViews.

And when we run the app, we will see the desired output on the screen and when we scroll the list to the bottom it will load the item from the second page and so on.

Getting the States of the data

Now, while loading the data we might also want to show progress view, and when we have loaded the data we should hide the progress view.

To react to the states of data we use addLoadStateListener like,

Here, we get the CombinedLoadState in addLoadStateListener and we use this to show or hide the progress views.

We can also react to errors inside the addLoadStateListener if all the state methods like refresh, append, and prepend are instance of LoadState.Error.

Now, since we have successfully integrated the Paging 3 library, now we will see how we can setup a footer view and header view.

FooterView is the one that when we go the last item of the list and the data is getting loaded from the next page, the footer view gets displayed. And as soon as the data is loaded the footer view is hidden.

For creating these views we will create a RecyclerViewAdapter similar to how we do in general use of RecyclerViews.

But in this case, we will extend the RecyclerView with LoadStateAdapter and pass the ViewHolder to it like,

Here, inside the onBindViewHolder, we get an additional parameter now of LoadState which we can use to show or hide the loading view based on if the data is getting loaded or not and add it to the adapter we use withLoadStateHeaderAndFooter like,

If we want to just add a footer we will use,

and to use with header we use,

And now, if the data is getting loaded from the paging library, the footer/header is displayed based on which one or both are attached to the RecyclerView adapter.

Using it with RxJava

If we don’t want to use it with Coroutines and Flow and want to go ahead and use RxJava, we still need to follow the above steps but with few changes.

Let me list them down.

Step 01.

First, we need to add the dependencies for Paging 3 Rx support,

and we will also add the dependency for RxJava2 adapter factory,

Step 02.

Now, as a next step, we need to update the PostDataSource and extend it using RxPagingSource in place of PagingSource like,

Now, the PostDataSource will implement the loadSingle function of return type Single like,

Step 03.

Now, we will update the APIService interface like,

Here, the getListData returns a Single and is not a suspend function anymore. And we have added another adapter factory to support RxJava2 to the Retrofit builder.

Step 04.

Now, inside the loadSingle function, we can do our tasks and return LoadResult.Page object if the output is successful or LoadResult.Error if it isn’t.

And to get the data from the PostDataSource to ViewModel we will update the listData variable in MainViewModel like,

This will return a Flowable of PagingData and their rest remains the same as above.

We can also pass make listData of type Observable of PagingData like,

The MainListAdapter remains untouched and in the setupView function in MainActivity gets updated like,

Here, we are passing the lifecycle of the View and the paging data to submit data to inflate the list.

This is how you can use the Paging 3 library with RxJava as well.

Conclusion

Paging 3 library is still in its early stage and it is completely re-written using Kotlin coroutines but it can be used with Flow and RxJava both.

Источник

Introduction to Paging 3.0 in the MAD Skills Series

Welcome to the Paging 3.0 MAD Skills series! In this article I’ll be introducing Paging 3.0 and highlighting how to integrate it into the data layer of your application. Want to watch and read? Check out the video for this episode here!

Why Paging 3.0?

Showing lists of data to a user is one of the most common UI patterns. When working with large datasets, you can improve app performance and reduce memory usage by fetching/displaying data asynchronously in chunks. This is a common enough pattern that having a library provide an abstraction that facilitates this is a huge boon. These are use cases that Paging 3.0 solves with aplomb. As an added bonus, it also allows your app to support infinite datasets, and if you’re pulling down network data, it offers avenues for local caching as well.

If you’re currently using Paging 2.0, Paging 3.0 offers a bunch of improvements over its predecessor including:

  • First-class support for Kotlin coroutines and Flow.
  • Support for async loading using RxJava Single or Guava ListenableFuture primitives.
  • Built-in load state and error signals for responsive UI design, including retry and refresh functionality.
  • Improvements to the repository layer, including cancellation support and a simplified data source interface.
  • Improvements to the presentation layer, list separators, custom page transforms, and loading state headers and footers.
Читайте также:  Как снять крышку андроид самсунг

Be sure to check out the Paging 2.0 to Paging 3.0 migration guide for more information.

Dropping it in

In the scheme of your app’s architecture, Paging 3.0 is best suited as a means of fetching data from your data layer, and ferrying it for transformation and presentation in the UI layers via the ViewModel . Access to your data layer in Paging 3.0 is through a type called the PagingSource , a class that defines how to fetch and refresh data around boundaries defined by a PagingConfig .

A PagingSource , similar to a Map , is defined by two generic types: that of its paging key, and that of the data loaded. For example, the declaration of a PagingSource that fetched Repo items from the page based Github API would be defined as:

The PagingSource needs to implement 2 abstract methods to be fully functional:

The load method

The load() method, much like its name implies, is called by the Paging library to asynchronously fetch data to be displayed. This is either for the initial load, or in response to the user scrolling around. The trigger for the call to load can be ascertained from the LoadParams object passed as an argument to the method. It keeps information related to the load operation including the following:

  • Key of the page to be loaded: If this is the first time that load is called (initial load), the LoadParams.key will be null . In this case, you will have to define the initial page key.
  • Load size: the requested number of items to load.

The return type of load is a LoadResult . It can either be:

  • LoadResult.Page : for successful loads.
  • LoadResult.Error : for when errors arise.

Note that by default, the initial load size is 3 * page size. This ensures that the first time the list is loaded the user will see enough items without triggering too many network requests if the user scrolls a bit. This is also something to consider when computing the next key in the PagingSource implementation.

The getRefreshKey method

The refresh key is used for subsequent refresh calls to PagingSource.load() (the first call is initial load which uses the initial key provided to the Pager). A refresh happens whenever the Paging library wants to load new data to replace the current list, e.g., on swipe to refresh or on invalidation due to database updates, config changes, process death, etc. Typically, subsequent refresh calls will want to restart loading data centered around PagingState.anchorPosition which represents the most recently accessed index.

The Pager object

With the PagingSource defined, we can now create a Pager . A Pager is a class that is responsible for incrementally pulling chunks of data from the PagingSource as requested by the UI. Since the Pager requires access to the PagingSource , it is typically created in the data layer where the PagingSource is defined.

The second thing needed to construct a Pager is the PagingConfig which defines parameters that govern how the Pager will fetch data. It exposes quite a few optional arguments that let you fine tune the Pager’s behavior, however the pageSize argument is required.

A brief description of the arguments utilized in the construction of the PagingConfig above follows:

  • pageSize : The number of items to be loaded at once from the PagingSource .
  • enablePlaceholders : Whether the PagingData returns null for items that haven’t been loaded yet.

We typically want the pageSize to be large enough (at least enough to fill the visible area of the screen, but preferably 2–3x that) so the Pager doesn’t have to keep fetching data over and over again to show enough material on the screen as the user scrolls.

Getting your data

The type produced from the Pager is PagingData , a type that provides a distinct window into its backing PagingSource . As a user scrolls through the list, the paging data will keep fetching from the PagingSource to provide content. Should the PagingSource be invalidated, a new PagingData will be emitted to make sure the items being paginated through are in sync with what is displayed in the UI. It may help to think of a PagingData as a snapshot of the PagingSource at an instance in time.

Since a PagingSource is a snapshot that changes when the PagingSource is invalidated, the Paging Library offers multiple ways of consuming PagingData as a stream including:

  • Kotlin Flow via Pager.flow
  • LiveData via Pager.liveData
  • RxJava Flowable via Pager.flowable
  • RxJava Observable via Pager.observable

This stream of PagingData is what the ViewModel can then opt to transform before presenting the paged items to the UI.

What’s next?

With the above, we’ve integrated Paging 3.0 into the data layer of the app! Stay tuned to see how to consume PagingData in the UI and populate our list of repos!

Источник

Оцените статью