MVVM using MediatorLiveData and Kotlin Flow
This article would be a short one as I am not going to dive deep into what MVVM or LiveData or Kotlin Flow is. We are going to look at what are the basic things needed in order to achieve MVVM and compare how we used to do it and with the new implementation with Kotlin Flow and MediatorLiveData.
Why Kotlin Flow?
Right after the stable releases of Kotlin Coroutines that had “suspending” functions that can asynchronously return a single value, questions were raised on how to return multiple values or a stream of data. Kotlin Flow came into the picture that solved the problem of handling streams just like how RxJava does. Flow is fairly new and consists of a considerable amount of experimental APIs inside which performs really good, so it is not recommended to use those functionalities in your production code just yet but that doesn’t stop us from experimenting with it.
Let’s say, you hit a network request from an app and it returns data preferably a list and you need to observe them through your MVVM data-flow. It involves creating LiveData everywhere and observing them in your Activity. Suspending functions allowed you to achieve that with ViewModel scopes but that is just not enough. You have to write another function to perform operations on those returned data and state handling is challenging. You can also do the same without Coroutines but you may need to include LiveData in your repositories which are not recommended, your interactors/repositories must never have Android dependencies, this is architecture 101.
Kotlin Flow comes to the rescue here by allowing you to get those streams of data and convert them to LiveData by using Kotlin “ktx” extension function (asLiveData(viewModelContext)). Your Repository function should return a Flow<Data> and the calling function in your ViewModel should convert them to LiveData for your Activity to start observing them. Now let’s see the difference between them in code.
I would never recommend this approach as it is a bad design to use LiveData in your repositories.
So, our next thing to work on would be how to remove the LiveData dependency and replace it with Kotlin Flow. Flow like RxJava has asynchronous capabilities and allows you to run a block of code in the background thread.
flowOn() is an extension function that allows you to specify the dispatcher on which the block should run on Dispatchers.IO should work in our case. There are a lot of operations available for us to use in the returned flow from the repository, you can collect them, specify onStart code block (what should be done before the flow block is executed), and convert the returned Flow to LiveData using asLiveData() extension function.
Now let’s look at how the code would look like when we use Flow with LiveData.
Note that your Activity code is left unchanged but your Repository and ViewModel have adapted to use Kotlin Flow. Flow is fairly simple and easy to adapt, most of all, you can do asynchronous operations without having to define a Coroutine scope every time.
MediatorLiveData
MediatorLiveData are used to merge multiple LiveData and enable a set of Transformation operations to perform on their results. Moreover, it helps you to ensure the state of the LiveData Observer that it observes only LiveData that doesn’t return stale values. Let’s take the ExampleActivity from the above example and look at what went wrong. You can see that viewModel.getData() is being called before the Observer code, so what happens when you switch them? Your observer starts to observe meaningless LiveData that returns nothing, so you see nothing on your screen. How do we solve this issue of code order conformity?
Well, MediatorLiveData comes to the rescue. It is a LiveData that observes other LiveData and returns a single stream of LiveData (one-to-many dependency). Now, let’s look at the changes that had to be made from the example to achieve this.
You can see that getData() can now be called after the Observer code which is convenient and you don’t have to worry about the order now.
Thank You for reading
That concludes this article. I believe I’ve covered the basics of MediatorLiveData and I hope that I was of help. Thanks for your time and please upvote if you liked the read, I’d appreciate it. You can find the sample project where I’ve implemented the concepts below.
Github Profile: https://github.com/tizisdeepan
LinkedIn Profile: www.linkedin.com/in/tizisdeepan
Happy Coding!