I watched the recent Android Dev Summit online and there’s one particular video that got my attention, a talk by Jose Alcérreca and Yigit Boyar on using LiveData with Kotlin Coroutines and Flow where they demonstrated how one could use LiveData builder with coroutines and Flow.

This got me thinking and I wanted to see if I could take this further and understand how these three things (Coroutines + Flow + LiveData) fit together.

Considering that you use an architecture similar to MVVM, here is a brief idea of how this works.

Your Repository layer would use coroutines to run long running code off the main thread. In addition to this you could use Flow to compose and coordinate multiple related operations; each of which in-turn call multiple coroutines. ViewModel calls this code confining the scope to its viewModelScope so that when this ViewModel is cleared; all running operations in this scope are cancelled. We then use LiveData to communicate the result to the View layer. Finally View’s call these method’s from ViewModel.

So how does this work code-wise? I have a sample code repository to show you just that. I encourage you to clone the repo, you will also need to get an API key from OpenWeatherMaps and run the app and see the application in action.

If you are not familiar with coroutines I would suggest a brilliant blog series by Sean McQuillan and for flow I would recommend to read the official documentation. I feel that they complement each other beautifully, in-fact Flow was designed to work seamlessly with coroutines. If you are familiar with reactive programming paradigm then you should feel right at home while using Flow.

Now let’s go through some of the key highlights of the application.

Repository layer

ForecastsRepository which uses Flow and coroutines to make network and database calls

Our ForecastsRepository has a public method getForecasts() which returns a flow builder, it checks if we should call the API and if that’s the case, it emits a suspend function getForecastFromAPI() which makes a network call using Retrofit. It otherwise returns cached data from our Room database by emitting another suspend function getDataOrError(). Notice the catch() operator, it catches all errors upstream, please note that it will not handle errors which might occur below it.

Note: We could return a simple suspending function from our Repository since the above operation is one-shot. I returned Flow here only to demonstrate it’s usage but please model one-shot operations as suspending functions in your projects. Flow’s should be used if you want to continue observing data after the first emission, for e.g. listen to your model changes from Room database.

There’s also a function named applyCommonSideEffects() which is an extension function, it applies some common side-effects like retryWhen(), onStart() and onCompletion() to our flow. Please note that all of these side-effect functions are experimental as of 25/11/2019 and they may be changed or removed in the future.

An extension function which applies some more commonly used side-effect methods to our Flow

ViewModel Layer

ForecastsViewModel which launches getForecasts using viewModelScope

We then have our ViewModel which calls the getForecasts() method from ForecastsRepository to get a result by confining the flow to it’s viewModelScope, additionally we use LiveData to communicate this result back to our View’s.

An important thing to note here is the collect() operator. Flow’s are inherently cold and they won’t emit data until you call a terminal operator on them, in our case this terminal operator is collect().

Also your Flow will be collected in the scope in which the collect() operator is called in. Notice how we call getForecasts() function confining our scope to the viewModelScope of this ForecastsViewModel. It is important to scope coroutine calls to the correct owner, so that the entire chain cancels when the owner is no longer alive (This concept is referred to as structured-concurrency).

Note: We could also scope getForecasts() to lifeCycleScope if we were to call it from our View.

View Layer

Finally we have our View, ForecastsFragment which just calls getForecasts() method from our ViewModel.

Note: You may want to observe changes to your entities from the Room database. I’m glad to inform that the developers of Room have you covered here. All you need to do is return a Flow<Entity> directly from your Room DAO and then subsequently collect the results by confining this flow to the scope of your View or your ViewModel, this is important as Room keeps the cursor open in order for you to observe changes from the Flow.

I hope that this article helps you to get started with Coroutines and Flow, as you’ve seen, it plays very well with MVVM architecture and I’m confident that it will work with other styles such as MVI, MVP and others.

Please visit my repository to get the entire source code and let me know your thoughts on the same.