Effective Networking On Android using Retrofit, Rx and Architecture Components
For many, like me, Retrofit is the go-to library on Android to talk to their REST APIs. Retrofit also supports adapters to convert your API responses into Reactive Streams. Reactive streams, among other things, help you handle threading effectively. This makes your Android application robust, stable and development faster.
For simpler use cases, the integration of Retrofit + Rx can be read about here and here. This advanced post tries to go to the next level and allow you to do the following:
- Integrate Rx + Retrofit + Android Architecture Components (AAC)
- Use a multimodule project with MVVM architecture
- Use Kotlin and its sealed classes to maintain state
- Handle errors effectively
- Respect the lifecycle of Android components.
You can find a sample app supporting this architecture here:
Implementation
Architecture
We follow an MVVM architecture with the AAC’s ViewModel helping our views to communicate with repositories
. All of these are glued together using Dagger 2. Our single source of truth is the local database on which we use AAC’s Room ORM
At the core of the architecture is the Outcome.kt
sealed class
The outcome
holds the state of the screen. It can be
- loading(isLoading) — where
isLoading
represents data lookup & can be used to show or hide a progress bar. - success(data) — where
data
is the data you would want to show on the UI - failure(exception) — where
exception
represents any error that could have failed the data lookup.
Flow
The outcome
object is observed at different layers in the following way:
- Events from your
activity
are passed onto theviewmodel
2. The viewmodel
relays the events to the repository
3. The repository
create a PublishSubject for the Outcome class
4. The repository
starts observing the contents of the local database, pushes it to the publishSubject.success(data)
and pings the server the first time to fetch remote update.
5. The remote sync on success, saves to the internal db. This triggers the publishSubject.success(data)
. On error, the error from the server is converted to an exception and pushed to the publishSubject.Error(exception)
6. The viewmodel
has a LiveData object observing the publishSubject of the repository
7. The activity
is observing the viewmodel
’s LiveData object and updates the screen contents accordingly
Conclusion
- Loading / Data / Error, all states are reacted to by the activity
- Exceptions from network sync can also be pushed as non-fatals to Crashlytics to debug later.
- Due to the ViewModel and Dagger Scopes, the lifecycles of the Android components are respected and there are no leaks of context as well as data.
- The ViewModel and Repository can be tested in isolation.
- Room can be replaced with ObjectBox.
Please feel free to play around with the sample app. Pull requests and issues are very much welcomed. Thank you!