A comprehensive guide to implementing Modern Paging on Android(JAVA)
As far as implementing this topic is concerned there are tutorials out there, but most of them are incomplete, with no ins and outs explained. Most of them don’t even discuss how can we show a loading spinner while data is being fetched. I have been through that hard time. So let me save yours!
So in this article I will discuss paging along with some advanced topics
- How to implement multiple view types
- Show a loading spinner when data is being fetched
And thats all there is to learn in this library.
Well, you can directly use the code to quickly plugin this feature into your app, or else you can continue reading to further understand the topic.
Let's start by quickly understanding what paging is?
So basically if you have thousands of items to show in your Recycler View, it doesn’t make sense to download all the thousands of items in one network call.
Reasons
- Will utilize too much bandwidth
- User can hardly see 5 to 10 items at a time
So that’s where we do pagination or paging, we only download a small chunk, for eg: 10 items at a time, and if the user reaches towards 10th item download the next 10 items and keep doing this.
At this point, if you think that “Oh!, Its quite simple to implement, attach a recycler scroll listener to the recycler view, and if the user reaches the end of the first 10 posts make a back-end call, fetch and load the next 10 posts and so on” Well, this is correct and this is how we used to implement paging on android before the paging library was introduced by google. So you might be thinking then why a library was introduced for this.
Well, there were some gotchas with the previous implementation!
- Lots of network call duplication
- Memory inefficiency
That’s why folks at google created a library specifically for this as a part of Android Jetpack Architecture Components.
Paging2 or Paging3?
Well, there is a Paging3 library introduced by google but that's a Kotlin first library and I tried to port it to JAVA but I was not able to as some of the Kotlin features were not present in JAVA
So for JAVA developers, I guess we have to go with Paging2 for now.
Disclaimer: If you are hearing Jetpack Architecture Components for the first time or if you are not aware of the terms in the below diagram, I would suggest you to start learning about these and come back.
Building android apps with Jetpack Architecture Components is a modern way of creating Android Applications that are efficient, performant, and has fewer lines of code
Before starting with Paging Library there is one fundamental topic I want you to be clear upon and that’s the evolution of Recycler-View Adapters in android
From the above classification, we can conclude that if you are still using recycler adapter for your recycler views you need to level up and start using a list adapter instead, as a list adapter forces you to write an efficient recycler view.
“But wait, we were implementing paging right then why this topic?” Because to implement paging we can’t use this simple recycler adapter not even a list adapter we have to use a PagedListAdapter and this is nothing but a further extension to the list adapter however with an identical implementation. So, now we’ve got our fundamentals clear now we can move on to understanding the Paging2 library
Paging2 library for Android
So there will be a total of 6 important components that we’ll have to understand and implement.
0. Dependencies
def paging_version = “2.1.2”
implementation “androidx.paging:paging-runtime:$paging_version”
1. Data Source
As from the name itself you would have figured out that this component is going to supply us the data for the recycler view.
So, from where is the data source gonna get the data? Well, that could be any source either from a web API or a local database.
However, in this library, we have 3 types of data sources and based on our use -case we have to choose one.
- Positional Data Source
We use this data source when we have to load data at arbitrary positions, i.e when there is a possibility that the user first loads the first 10 items(1–10) and then jumps to a position where it should load the next 10 items starting from let's say 70th item(70–80).
for eg: A contacts app.
2. Item Keyed Data Source
We use this data source when the next chunk of data to load depends on the last item in the current chunk loaded.
3. Page Keyed Data Source
We use this data source when the chunks are given page numbers.
For eg: see the below animation
For this article, I am going to use the page keyed data source as it is very commonly implemented in most APIs out there. The API that we are going to use for this implementation is a custom JSON Placeholder API. However, you have to just change the URL according to your API endpoint rest all will be the same.
https://jsonplaceholder.typicode.com/posts?page=1
This API returns a list of 20 posts based on the page number provided as a query parameter.
Lets look at the DataSource class,
PostsDataSource extends PageKeyedDataSource which has 3 methods that we have to override
And in all these 3 methods
We have to perform 2 basic steps,
- Make and API call
- Once the response is available pass it to the callback.onResult(…) method
Lets look at these 3 methods now,
- loadInitial(..): This method is called only once and is called to fetch the first page.
- loadAfter(…): This method is called to make an API call to download the next page data (after the current page) as the user reaches towards end of the current page.
- loadBefore(..): This need not be implemented as previous pages have already been loaded.
To show a loading spinner when data is being fetched we have a MutableLiveData that holds the loading state.
Based on this loading state value we will later in our PagedListAdapter decide which viewType to show,
- Loading state is ONGOING or FAILED we will show a LoadingView.
- Loading state is SUCCESS we will show the PostView.
2.Data Source Factory
This component is there to create an instance of the above-selected data source in our case a PageKeyedDataSource instance
Why not create the instance directly using the new keyword? Well, this has to do with implementing clean architecture using factory design pattern in java. (Using a factory class to instantiate an object is one of the 23 design patterns)
Well, the code is commented well, and is self explanatory.
3. Repository
Well, if you have ever implemented MVVM in android you might know about this component. Still, for those who don't know, this component's main functionality is to abstract away the implementation details of how to fetch the data and whether to fetch data from the internet or local database.
In our implementation, this will create a PostDataSource instance using PostDataSourceFactory create(..) and will return an instance of a PagedList based on the PagedListConfig passed.
PagedListConfig is basically the configuration of how many items to download in each page which is decided by pagesize, how many pages to fetch for the first load of Recycler-View items and many more.
Paged list is like an ArrayList (as this also implements AbstractList) which holds chunked data (pages) from a data source.
4. ViewModel
This component is the mediator between the data layer (repository) and the UI layer (activity/fragment).
It calls the repository to get the data as a paged list and pass it to the activity or fragment.
5. Activity/Fragment
This component is responsible to get the data as a paged list from the view model component and create an instance of a PagedListAdapter and pass the paged list that it receives from the ViewModel to the PagedListAdapter.
6. PagedListAdapter
As we discussed at the start of the post that we will have to use a PagedListAdapter to implement paging using the Paging2 library.
Well, the implementation is identical to RecyclerAdapters implementation hence doesn't need much explanation.
However as promised at the start of article we will discuss how to implement multiple viewTypes and show a loading spinner when data is being fetched
Well getaItemViewType(..) method is used to implement multiple viewTypes.
For eg: in our implementation we are using this method to show 2 viewTypes one is the Post view and the other is loading view
- If we are at the last position of current page and loading state is either ONGOING or FAILED we show a loading spinner
- Else we will show the Post view
Now let’s have a look at the overall data flow
- The ViewModel triggers the repository method to create the PostDataSource using the PostDataSourceFactory.
- The data is fetched by the DataSource and is passed on to DataSourceFactory.
- BoundaryCallback is a function that triggers fetching of pages for the first time it calls loadInitial (…) and then loadAfter(…) method as the user reaches to the end of the page being displayed each and every time.
- The data is in turn passed to Repository and a PagedList is built out of it.
- This PagedList is then passed on to the UI layer through the ViewModel where the PagedListAdapter takes care of displaying the data.
Finally let’s have a look at our folder structure
1. Adapters will have the PostRecyclerAdapter, LoadingViewHolder, and PostViewHolder classes
2. The model folder will have PostDataSource in the datasources folder, POJO for a Post in entities folder, and PostRepository in the repositories folder.
3. The network folder has the Api interface and RetrofitClient implementation
4. The viewmodel folder will have the MainActivityViewModel and MainActivityViewModelFactory classes
5. The views folder will have the MainActivity class
6. Constants file to hold any constants to be used throughout the project. We are currently storing the load state constants i.e ONGOING, FAILED, and SUCCESS
Well, I have discussed all the fundamentals along with 6 important components that you will need to implement to perform paging.
However there are few things that I haven’t discussed as they are pretty easy and fall under normal android development topics,
- The retrofit implementation for performing network calls
- The XML for activity/fragment or the recycler view item
- The MainActivityViewModelFactory class to instantiate the MainActivityViewModel
So, hope you liked this. If you have any suggestions or improvements that I should do in my articles let me know them in the comments section.