Build an app with offline support: exposing network states

In part 1 we have developed an app that used Room to cache the data and to be functional (partly) when there is no connection. The problem with the previous approach was that our app omitted network error and loading states. In this part we are going to implement a generic NetworkBoundResource helper class to expose the missing information.
NetworkBoundResource starts by observing database for the resource. When the entry is loaded from the database for the first time, NetworkBoundResource checks whether the result is good enough to be dispatched and/or it should be fetched from network. If the network call completes successfully, it saves the response into the database and re-initializes the stream. If network request fails, we dispatch a failure directly.
A closer look at the NetworkBoundResource
The NetworkBoundResource class is an abstract class with the following abstract method:
- saveCallResult: This method is responsible for updating/inserting the result of the API into the local database. This method will be called when the data from the remote server is successfully fetched.
- shouldFetch: Based on implementation, this method should return true if it is needed to fetch the data from a remote server. For example when the data is outdated. Else it should return false.
- loadFromDb: This method is responsible for returning the data from the local database.
- createCall: This method is responsible for creating a remote server call which is responsible for fetching the data from the server. Later we will see how to wrap this result in LiveData.
After implementing these abstract methods, we can call the getAsLiveData method of NetworkBoundResource class, which returns a LiveData object that can be observed for changes.
Under the hood

When a new instance of NetworkBoundResource is instantiated, it creates an instance of MediatorLiveData, sets the ResultType to LOADING and starts observing the results from the database (it calls the loadFromDb method). Based on the result from the database, it checks if it is needed to fetch data from the server (the shouldFetch method). If it is not the case then it sets ResultType to SUCCESS. If the result of shouldFetch method is true then it will fetch the data from the server (using the createCall method). Based on the result from the server, it sets the ResultType to SUCCESS and calls the saveCallResult method to update the database. Or it sets the ResultType to FAIL if the result from the server is not successful.
Usage
Before being able to use it, we have to modify GitHubApi to return LiveData. This is easily done, we can simply create our own CallAdapter, which converts the Call into a LiveData of ApiResponse.
We also need a LiveDataCallAdapterFactory
And now we can simply add this factory to our Retrofit.Builder.
Now we are ready to use our NetworkBoundResource in our Repository
That is it, all we have to do is to implemented the abstract methods as shown above.
Get the source from the GitHub. For this part you can look at:
· MainActivity2
· MainActivity2ViewModel
· GitHubRepository2
Conclusion
Now our app is extended by exposing the network states; such as loading, succes and error. The app shows the cached data, and starts retrieving the data from the backend. Meanwhile, it shows a non-blocking loading animation. Once the data is fetched from the server, it updates the database and notifies the UI to update the data and its state.