Portal Berita dan Informasi Terbaik di Indonesia

Google News clone in Kotlin using Paging 3 & Hilt | by Divyansh Saraswat | Apr, 2022

Photo by Obi – @pixel6propix on Unsplash

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.

Gradle dependencies:

In the Project level build.gradle:

Hilt Setup

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 @Provides.

@Singleton scopes this instance to the Application container and makes it always provide the same instance.

The object AppModule is annotated with @InstallIn(SingletonComponent::class) which means that the bindings defined in this module are available in the Application container.

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 combine operator.
  • 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 Pager.flow using the flatMapLatest operator.
  • Cache the stream of data in viewModelScope using the cachedIn operator 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 PagingDataAdapter.

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.

Collect the 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 getRefreshKey function 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.

Leave a Reply

Your email address will not be published.