LiveData with Coroutines and Flow — Part III: LiveData and coroutines patterns

Jose Alcérreca
Android Developers
Published in
4 min readJul 13, 2020

--

This article is part III of a summary of the talk I gave with Yigit Boyar at the Android Dev Summit 2019.

LiveData with Coroutines and Flow (ADS 2019)

Part I: Reactive UIs

Part II: Launching coroutines with Architecture Components

Part III: LiveData and Coroutines patterns (this post)

ViewModel patterns

Let’s look at some patterns that can be used in ViewModels, comparing LiveData and Flow usages:

LiveData: Emit N values as LiveData

If we don’t do any transformations, we can simply assign one to the other.

Flow: Emit N values as LiveData

We could use a combination of the liveData coroutine builder and collect on the Flow (which is a terminal operator that receives each emitted value):

But since it’s a lot of boilerplate, we added the Flow.asLiveData() extension function, which does the same thing in one line:

LiveData: Emit 1 initial value + N values from data source

If the data source exposes a LiveData, we can use emitSource to pipe updates after emitting an initial value with emit:

Flow: Emit 1 initial value + N values from data source

Again, we could do this naively:

But if we leverage Flow’s own API, things look much neater:

onStart sets the initial value and doing this we just need to convert to LiveData once.

LiveData: Suspend transformation

Let’s say you want to transform something coming from a data source but it might be CPU-heavy so it’s in a suspend function.

You can use switchMap on the data source’s LiveData and then create the coroutine with the liveData builder. Now you can just call emit on each result received.

Flow: Suspend transformation

This is where Flow really shines compared to LiveData. Again we can use Flow’s API to do things more elegantly. In this case we use Flow.map to apply the transformation on every update. This time, because we’re already in a coroutine context, we can call it directly:

Repository patterns

Not much to say about repositories, as if you’re consuming a Flow and exposing a Flow, you just use the Flow API to transform and combine data:

Data source patterns

Again, let’s make the distinction between a one-shot operation and a Flow.

One-shot operations in the data source

If you’re using a library that supports suspend functions, like Room or Retrofit, you can simply use them from your suspend functions!

However, some tools and libraries do not support coroutines yet and are callback-based.

In that case you can use suspendCoroutine or suspendCancellableCoroutine.

(I don’t know why you would want to use the non-cancellable version though, let me know in the comments!)

When you call it, you get a continuation. In this example we are using an API that let us set a complete listener and a failure listener so in their callbacks we call continuation.resume or continuation.resumeWithException when we receive data or an error.

It’s important to note that if this coroutine is cancelled, resume will be ignored so if your request takes a long time the coroutine will be active until one of the callbacks is executed.

Exposing Flow in the data source

Flow builder

If you need to create a fake implementation of a data source or you just need something simple you can use the flow constructor and do something like this:

This code emits a weather condition every two seconds.

Callback-based APIs

If you want to convert a callback-based API to flows, you can use callbackFlow.

It looks daunting, but if you split it apart you’ll find that it makes a lot of sense.

  • We call offer when we have a new value
  • We call close(cause?) when we want to stop sending updates
  • We use awaitClose to define what needs to be executed when the flow is closed, which is perfect for unregistering callbacks.

In conclusion, coroutines and Flow are here to stay! But they don’t replace LiveData everywhere. Even with the very promising StateFlow (currently experimental) we still have the users of the Java Programming Language and Data Binding to support, so it won’ t be deprecated for a while :)

Some links if you want to read more:

My other blog posts about LiveData

--

--

Jose Alcérreca
Android Developers

Developer Relations Engineer @ Google, working on Android