News App: Clean Architecture with MVVM + Room DB with Pagination + Other Android JetPack Components.

Kanchan Pal
ProAndroidDev
Published in
6 min readNov 29, 2020

--

The blog demonstrates above by creating an Android app from scratch.

Photo credit unsplash.com

So if you’ve landed on this page, I am sure you are very curious to build a very responsive, fast and reliable Android application from scratch with latest/widely used tools and technologies out there. Without wasting time let’s directly get started on this 🚀 🔥 🔥 🚀

Please visit the above Github Link to check what this app is gonna look like with its inner (Tech-stack) and outer beauty (layouts and UI/UX).

Requirements of the Application :

We will build a news application which fetches data from an open source News API. We will persist data in room database so its visible offline also. Next time when you’re online it shows data from DB while fetching fresh news in background and updates DB, thus UI is notified to render the changes.

Except that to make app fast and responsive for huge set of data we include pagination (fetching more data on demand else not) even in offline mode.

Architecture : This app is inspired and based on Guide to app architecture and follows MVVM (Model-View-ViewModel) architecture.

Model: News Data model will look like as follows. We use @Entity annotation so Room DB identifies it as a table for storage purpose.

View: NewsListFragment and NewsDetailFragment (Activity/Fragments)

ViewModel: It will handle all the business logic for fetching data and preparing the data to show on Activity/Fragment. UI layer will have no information about the logic, it would be only responsible to render the data contained in ViewModel by simply observing its changes.

Let’s Get Started ….

Though I am not in very favour of data binding, just included this if anyone wants to look how it works. So our main activity contains two fragments NewsListFragment and NewsDetailsFragment and it looks like following.
Also, we are using NavigationComponent from JetPack. This blog doesn’t cover Databinding or Navigation Component working explanation. May be in another blog we can have dedicated focus on these.

Similarly, we will design a layout for both the fragments of the given activity. Please find the link for other layouts we’ll be using in this application.

Network Layer Handling:

We’ve used Retrofit library for networking, so our NewsServiceInterface is as follows along with the response model.

Single Source of Truth:

So here comes my favourite part. We make a layer called NewsRepository which is responsible to fetch the data based on remote (network) or local data source (Room database). ViewModel asks the repository for the live data which other UI components listen to and render themselves accordingly. The following diagram shows this flow:

Our Local database acts as a single source of truth to avoid any discrepancy in data throughout the app. When a user opens the app, the repository shows data already persisting in DB while fetching fresh information from backend and first update DB. Once DB is updated, then only UI would be updated via fresh DB data, but never directly from remote source. That’s why we call Local Database a Single source of truth.

Working flow for Single Source of truth:

NewsRepository: Let’s have a look on the repository how it provides data to view model the way it is explained above. We will discuss NewsDao and NewsRemoteDataSource class just after this.

NewsDAO: It helps access DB data. Room supports LiveData as return type for objects that’s why when fresh data is updated from remote to DB via insertAll(), UI component is again refreshed as getNews() is returning live data. Upon its data change, UI listeners are notified. Check getPagedNews() method, will later explain for offline pagination.

For fetching data from remote, we have following class which is used in repository constructor from remote calls.

ViewModel: As discussed earlier, ViewModel is shown below asking the repository for the live data which would be eventually observed in UI.

Finally NewsListFragment can observe this live data and update UI itself.

That’s how we see full flow of data from remote to db to UI.

Pagination Concept:

While this flow seems good and fascinating, the challenges come in real problems when your data from the network is way too big. You would like to query data in chunks. Now since you don’t get data in one shot, you can’t update DB with it in one shot. Here are the changes to implement pagination.

View Model : Here similarly as above we ask repository for the data. Notice we observe paged news this time.

We have created a custom Dataclass (used above) as follows which has pagedList live data (in order to render data in chunks) and a network state based on which we show and hide progress bar on UI .

Tracking Network State used above is as follows

We require changes in NewsRepository in order to implement pagination and return Data to ViewModel while observing local and remote news list.

For offline pagination we require observeLocalPagedNews() method explicitly. In offline flow, the repository asks data from local DB via DAO using it’s getPagedNews() method which has return type DataSource.Factory<Int,NewsListModel> as shown and mentioned earlier in DAO.

LivePagedListBuilder (in-built class) is used to build known live data of paged list using DataSource.Factory<Int,NewsListModel> . This obtained live data from LivePagedListBuilder is eventually listened in UI.

For online Pagination we create NewsPageDataSource which implements PagedKeyedDataSource where API end points are used to scroll data back and forth. When paged response is successful it is updated in DB via insertAll() method as shown in fetchData() method below.

We’ve created a wrapper sealedResult class to define different states of data as follows:

LivePagedListBuilder:

Let’s check out this class which we’ve used in NewsRepository. It takes DataSource.Factory and PagedList.Config as parameter in its constructor. As mentioned above already, it will build live data of paged list using DataSource.Factory.

Room has in-built support for pagination as it can return data wrapped up in DataSource.Factory<Int,ModelType> as shown above in NewsDao. Here Int is just a key identifying items in DataSource. Without pagination we had instead returned data wrapped in LiveData<NewsListModel> which was observed all the way down to UI component.

Now coming to PagedList.Config, this is provided by JetPack Components to configure the pagination we require as per our use case. For eg, the number of elements to load in one shot, how many items to keep in buffer before and after while scrolling etc. Following class shows how we create such configuration.

Finally these two parameters (DataSource.Factory<Int,ModelType and PagedList.Config) are passed into LivePagedListBuilder class and use its build() method to actually return a live data of paged List. Check NewsRepository where we have used this class.

Now here we are good to connect all the dots. We see how NewsRepository is fetching data from remote and local DB with pagination and ultimately sending it to ViewModel wrapping in our custom class called Data which has LiveData<PagedList<NewsModel>> and LiveData<NetworkState>.

NewsListFragment only observes the data from ViewModel as usual but this time a pagedList live data. You can see subscribeUI() method in the code below.

You can see above, that UI is observing a pagedList (A class in android jetpack) live data instead of a simple ArrayList live data.

If you observe carefully, NewsListFragment only observes this live data of PagedList<NewsModel>. Now since this is not a simple ArrayList of data, we don’t use a regular RecyclerViewAdapter to render this data. We use our NewsAdapter which implements PagedListAdapter (in-built class) which has a submitList() method where we simply feed in our paged list being observed by NewsFragment above. NewsAdapter looks as follows.

Also, working explanation and set up Room DB wasn’t scope of this blog but what needs to be mentioned is a quick look to build our Room DB in the app.

Henceforth, we complete the flow of an application with a well-versed architecture supporting offline data persistence with pagination. To keep our use case simple we implement offline and online pagination separately thus bypassing SingeSourceOfTruth.

Your claps on this article and starring to github project would be highly inspiring for next tutorials, if you find this one helpful for your app setup.

Get an API key if you want to write project of your own using this API. You can check out the full source code below.

Happy Learning and Sharing!! 🤟🤟

--

--