Learn what’s involved to implement pagination in a clean-architecture news app.
This is what the final implementation will look like in the end:
The key ingredient is JetPack’s Paging 3 library.
In the Project level build.gradle:
Don’t forget to add the application name in the manifests file.
Now, we define the interface that contains retrofit-methods which make network requests.
Dependency Injection with Hilt Modules:
Note that Retrofit code uses builder pattern.
Interfaces and classes which use builder-pattern can’t be constructor-injected. Therefore, we need to create modules that are instructions to dagger-hilt on how to provide such dependencies.
We provide retrofit instances by creating functions annotated with
@Singleton scopes this instance to the
Application container and makes it always provide the same instance.
AppModule is annotated with
@InstallIn(SingletonComponent::class) which means that the bindings defined in this module are available in the
Define the source of paging data:
This class also contains the logic of calling the methods in
NewsApiInterface based on query parameters declared in the constructor.
When the ‘query’ param is null, we intend to get top headlines based on ’country’, ‘language’, and ‘category’; whereas a non-null ‘query’ indicates a search in the news-API.
In this way, we abstract the logic of making network request based on query parameters from the repository layer.
In the ViewModel layer, we transform the data emitted by
Pager.flow based on queries received from the UI layer.
Flow is an asynchronous sequence of values.
Values are emitted by
Pager.flow, transformed by intermediaries declared in the ViewModel, and ultimately consumed in the fragment.
The ViewModel part goes like this:
- Combine the query-flow and sortParams-flow using the
- Save the latest values of these streams in a data class and return them from the lambda.
- Switch from the above flow to the repository’s
- Cache the stream of data in
cachedInoperator from the Paging library
Till now we have defined the flow of data to the ViewModel layer, now let’s define how this data will be consumed in the UI layer.
This step updates the fragment’s recycler view, based on the observable stream of data.
Let us define a class that extends
The time at which a particular news article was published is subtracted from the current date-time of the system (android device) to get ‘x no. of hours ago’, ‘x minutes ago’, and so on.
The implementation is as follows:
Check out this article by Jonathan Darwin to learn more.
Flow<PagingData> in the Fragment.
- Instantiate the adapter class and assign it to a variable
- Set this variable to the fragment’s recycler view adapter
- Collect the flow from the ViewModel and submit the stream of data to the adapter.
That’s it, we have defined all the necessary components of the Paging library and implemented search functionality just like Google News.
Similar steps are to be followed when implementing fragments for top headlines (see the paging source implementation above).
Paging 3 library helps us to load data from a remote/local data source in small chunks called pages, thus allowing the app to use network bandwidth and system resources more efficiently.
But this approach involves a lot of boilerplate code.
You must have noticed that the above approach often complicates stuff, for example, if the page key is of type string and not Int.
In this case, we custom-define the
getRefreshKeyfunction in our paging source to update the page key (often called page-token, like in YouTube Data API) which is now part of the network response.