Network awareness using LiveData

The most exciting thing I got from latest 2017 Google I/O was the inclusion of Kotlin as officially supported language in Android. And the second one was the presentation of Android Architecture Components.

One of the subjects with more hype in Android during last years has been Clean Architecture. Different architectures has been discussed in forums, lectures and blogs. MVP, Hexagonal, MVVM… Can you see the issue here? Architecture fragmentation :)

Android Architecture Components are a well-thought, although still in Alpha stage, way of create applications in Android. Google recognizes that this is not the only correct way of doing things well, and that if you are currently using other Clean technologies you don’t need to switch. But it is worth to analyze those new components in order to learn from them. Also, it would be a good idea to give those components a try in your next brand new project.

The main components introduced are:

LiveData : Observable that contains the data and is Activity lifecycle aware, avoiding refreshing data on background or killed activities.

: Observable that contains the data and is Activity lifecycle aware, avoiding refreshing data on background or killed activities. ViewModel : Contains the LiveData objects for the UI component and is responsible of loading and handling the data from the source. It is independent of the activity and survives configuration changes like device rotations.

: Contains the LiveData objects for the UI component and is responsible of loading and handling the data from the source. It is independent of the activity and survives configuration changes like device rotations. Room: ORM that manages the SQLite database in an easy and error safe way. Room can return database data as LiveData observables.

Additionally, now Google recommends using OkHttp 3 / Retrofit 2 for network access and Dagger 2 for dependency injection.

Google has published several explanatory articles and documentation about those components, there are also two interesting code labs about lifecycle and room.

But was seems to have been left aside until the last moment at least, is network awareness and error management. In the Architecture documentation there is an addendum about exposing network status.

The idea is to pass not the data object in the LiveData but a wrapper object that contains the data and the network state. The problem is that data comes from two different sources, Database (Room) and Network (Retrofit) in an asynchronous way. To achieve this, the proposed solution is an intermediate object that observes network and database, creating a new observable for the upper layers and managing the refreshing logic.

In my opinion this new object (NetworkBoundResource class) has too much responsibility and unnecessarily complicates things. Data and network state are not the same thing, specially when data comes from various sources, not only from the network.

I have created a small application for testing the architecture components in Kotlin: Karchitec. It is a very simple RSS reader that conforms to Google architecture with a few small modifications in order to maintain things as independent and simple as possible.

Activities are only responsible for communicating with the Android Framework, obtaining the ViewModel and pass it to the Views (decorators).

are only responsible for communicating with the Android Framework, obtaining the ViewModel and pass it to the Views (decorators). Decorator : Used as the View of MVVM, instead of a Fragment. It subscribes to LiveData observables in the ViewModel and communicates with the presenter for non data related app logic.

: Used as the View of MVVM, instead of a Fragment. It subscribes to LiveData observables in the ViewModel and communicates with the presenter for non data related app logic. Presenter : Not really needed on this simple example but I like to extract app logic, like navigation decisions, from the Decorator. On bigger apps, presenters are a must.

: Not really needed on this simple example but I like to extract app logic, like navigation decisions, from the Decorator. On bigger apps, presenters are a must. ViewModel : Holds and manages the observables (LiveData), persisting them over activity configuration changes.

: Holds and manages the observables (LiveData), persisting them over activity configuration changes. Repository : Central point for data access. It insulates underlying data sources from the app hiding the details.

: Central point for data access. It insulates underlying data sources from the app hiding the details. DAO : Manage the Domain Model objects and local database storage.

: Manage the Domain Model objects and local database storage. API Use Cases : Manages network access, and API Model objects.

: Manages network access, and API Model objects. Mappers: Map from API Model to Domain Model

Network state

What we want to achieve is to allow any class interested in knowing how the network access is going to subscribe to that info. And having the concept of LiveData Observable, we can implement this network state flow easily making it Lifecycle aware at the same time. Let’s see how the RSS reader app implements this.

The ViewModel contains two separate LiveData observers, one for the actual data and other for the network status. For the sake of simplicity only those states are managed:

enum class NetworkError{

SUCCESS,

DISCONNECTED,

BAD_URL,

NOT_A_FEED,

UNKNOWN,

}

Use case reports back those states to the repository by means of a function callback:

executor.execute {

try {

val response = feedService.getFeed().execute()

val channel = channelMapper.map(response.body)

channelsDao.insert(channel)

callback(NetworkError.SUCCESS)

} catch (e: Throwable) {

when (e) {

is UnknownHostException -> callback(DISCONNECTED)

is IllegalArgumentException -> callback(BAD_URL)

is XmlPullParserException -> callback(NOT_A_FEED)

else -> callback(NetworkError.UNKNOWN)

}

}

}

How is this managed in the Repository?: addChannel() method calls the use case and receives the result in its callback function. Then it adds the returned network state to a LiveData (errorData) observable. Observers can get both the data and the network state observables from the repository:

fun addChannel(link: String) = useCase.refreshItems(channelLink,

{ errorData.setNetworkError(it) }) fun getChannels() = channelsDao.getChannelList() fun getErrors(): LiveData<NetworkError> = errorData

The errorData member is an instance of ErrorLiveData class that extends from LiveData and adds only one method for setting the network state value in the correct thread. The set value will then be sent to all observers:

class ErrorLiveData

@Inject

constructor(

val executor: MainThreadExecutor

) : LiveData<NetworkError>() {

fun setNetworkError(value: NetworkError) {

executor.execute {

super.setValue(value)

}

}

}

Finally, the Decorator (MVVM View) observes both streams. In this case this behaviour results convenient, but perhaps in other situations the network state stream can be observed from the presenter, while the decorator is only interested in the data.

viewModel.channels.observe(activity, Observer<List<ChannelUiModel>> { showChannels(it) }) viewModel.errors.observe(activity, Observer<NetworkError> { showErrors(it) })

You can find the complete project in my GitHub repository. Please leave your thoughts in the comments.