RESTful API consuming on Android using Retrofit and Architecture Components (LiveData, Room and ViewModel)
During my Udacity Android developer nanodegree, I faced the need to develop some apps that consume RESTful APIs. Searching online for the best practices of effective networking I read multiple articles and concluded to a basic model following the guide by Google for the use of the new architecture components.
LiveData (an easier but limited Google’s alternative to rxjava) together with ViewModel provide reactive features to the app design, are life-cycle aware solving common problems with configuration changes(like screen rotation) that lead to memory leaking or crashes, and if implemented correctly increase the “testability” of the code. My solution described below is a simplified version of the GithubBrowser sample by Google.
The code of this article is in the restapi-tut branch of this repository. I’m using dagger2 for dependency injection (could be omitted), the popular networking library Retrofit with gson for json parsing, Room persistence library for easier SQLite db use, okhttp http client and stetho adb for network and db debugging.
I’m trying to implement an MVVM architecture, using ViewModel with a repository that is responsible for fetching the online data, storing them in the SQLite db, providing them to the ViewModel and with its turn to the view (Activity or fragment). I know it sounds a little complicated so here is a picture describing the above logic.
The app has a single source of truth and this is the internal db, the data has to be saved first or refreshed before they are displayed to the ui.
The Resource class is a wrapper class above the data that has two extra fields the outcome of the online data fetch and an optional message. A Resource can have status:
i) success
ii) fail (can’t fetch from internet) or
iii) loading (trying to download online)
and may or may not have data. The second case (no data) can help to check whether to show/hide a progress bar (loading) or close the app/display a message if there aren’t any data in db and can’t fetch online (fail).
Example events sequence
- Events from the view get sent to the ViewModel and the view observes LiveData from it.
2. The ViewModel asks and observes LiveData from the Repository, acting like a “mediator” (MediatorLiveData) to the view
3. The repository changes its LiveData to Status.Loading, and asks for data from the db. We can show a progress bar to notify the user about online data fetching.
4. If there are data stored in the db the repository loads it and changes the observable LiveData data field leaving the status to loading, in order to show the stored data to the user and let him know that we continue online fetching in the bacnkground. Every CRUD action on the db using Room Library is made using AsyncTasks to make sure not to cause any ANR from holding the main thread.
5. If the online call is successful the db is notified and the obserbable’s status is changed to success, else it will update only the status to error.
6. The call to loadDataFromDB will result to update the only observable’s data leaving the status success.
Conclusion
With proper checking of the changes on LiveData from the ViewModel or the Activity/Fragment we can handle every use case, like having new online data replace the old from db, or fresh data after clean install, or even errors that should close the app (no data to show) or just notify (the user or any analytics/logging library that we use) that the data we show are not refreshed.
Other useful articles, that helped me with the above concepts: