Building My First MVVM With Kotlin Coroutines

Adalberto Plaza
The Startup
6 min readDec 13, 2020

--

I’ve been working with MVP (Model View Presenter) and Executors for a long long time. Probably more than I actually wanted. But you know, Android world is always changing and there’s no physical time to try out every new “suggested” architecture. Besides it’s not feasible migrating your profesional projects every time a new technology is out. One just go step by step.

Said that, this new approach looks like it’s here to stay (because of all its benefits), so the time to give it a go came!

In this post I’m going to describe the architecture in general terms in order to be able to have a first look if you are wondering “what is this all about”. This is not an exhaustive manual, but I’ll try to give you some good resources as well. Some basic code is shown as well.

Disclaimer: the code will be a very simple one in order to better understand the concepts. For instance, Repository, UseCase and so on must implement interfaces for a better consistency. But I’m skipping those patterns.

Coroutines:

In a few words, Kotlin coroutines are a way of handling concurrent processing with asynchronous jobs. Actually they are a smart layer wrapping threads pools, but they facilitate the management quite a bit. So forget about thread creations, swapping, blocks and so on.

They are very lightweight compared with pure threads, and one of the biggest advantages is that one single thread can run different jobs at the same time. This is because when a block of code is waiting for an external result (or another thread), it gets suspended and the current thread is used for other tasks on that period. Moreover, we don’t need complex callbacks between threads to communicate them, since this is handled automatically in the code.

This is specially useful in Android threading. Using coroutines in Android, allows us to use the UI thread in a very direct way. Think in the following simple scenario: we need some data from an external source (network or database) to show it inside a list.

You will need thread creations, callbacks and thread swapping to correctly handle it in a conventional scenario, but the solution is pretty straight forward when done with coroutines.

// function called from UI Thread
fun onButtonClicked() {
viewModelScope.launch {
val dataList = getDataFromNetwork()
showDataInView(dataList)
}
}

This is a bit of pseudocode, but those 3 single lines (called from the UI thread) are getting the data from the network, not blocking the Ui thread, and showing it, when retrieved, using the UI thread again. Here the magic relies in the viewModelScope. A picture is worth a thousand words, so think about it as the following one:

Coroutine with UI thread example

The architecture:

The idea behind this architecture is to “keep it simple”. We will be working with some familiar layers like Repositories or UseCases. But the point is that both MVVM and Coroutines make everything smoother.

MVVM arqchitecture used in this example

Let’s start from the inside to the outside:

Datasource: the key point here is that we will be using a special functions called suspend functions. They are called like that, because they allow being “suspended” while they are waiting for a result. (Remember coroutines are able to suspend jobs and re-use the current thread while waiting). Keep that concept in mind. In this case, we could use Retrofit for network requests or Room for getting the data from a local database. In both cases, each library provides us the correspondent suspend functions. So we don’t really need to worry about them right now.

@GET("/enpoint/")
suspend fun getData(): Data

Repository: this layer will remain very typical in this example. It will just get calls from the UseCase, calling the DataSource and returning the result to the first one. It can cache the data as well. But the most important thing is that iy has to keep the suspend modifier. This is, because a suspend function (in datasource) can only be called from a suspend one, or from a coroutine scope. We are not introducing any coroutine yet in this layer, so we need to keep the suspend.

UseCase: here is where things get interesting. The use case should know about our business logic. So let’s take that advantage to talk about Dispatchers. We know that the data is get from the network, so we know for sure it’s an IO operation which can take several milliseconds. In this case we are not doing anything else in the use case than retrieving the data, maybe implement a retry mechanism, but that’s it. in addition to that, we are going to specify the way we consider our calls should be handled, and we will do that using a specific Dispatcher. A dispatcher, is basically a way to specify in which threads (or in which internal thread pool) we want to run the code. There are two main dispatchers: Default and IO. Default is designed to run heavy tasks and the number of threads in there are limited. IO is designed to run tasks which need to perform Input/Output operations. Those operations typically require “long” waiting times. The reason why this second dispatcher has a bigger number of thread available. (Remember that tasks can be suspended while wait, reusing the current thread)

ViewModel: the very best definition for this layer, is the one developer.android gives, “it’s a class designed to store and manage UI-related data in a lifecycle conscious way”. Putting it in other words, it’s a layer where put and work with all the information we need to show to the user in the UI. But what’s different from a presenter? You already read it: you don’t need to be aware of the lifecycle of the View. In addition in this layer we will be using LiveData objects. They are observable objects with superpowers. Since, in the same way ViewModel is aware of the View life cycle, they are too. So these two classes make a perfect couple: ViewModel handles the communication with the View by getting events from it, and updating the data using LiveData objects it has inside once the data is processed. The the view just has to observe the LiveData objects and change the interface depending on their states. Note that, since the ViewModel and the LiveData are aware of the lifecycle, they won’t be active if the view is not active either.

Coroutines in the ViewModel: they will be in charge, as mentioned before, to call the UseCase while hadling the execution flow of the code. Putting it very simple, the ViewModel gets the call from the View (an event triggered by the user which can be tap a button for instance) and will call the suspend function without blocking the UI thread. Then when the suspend function has finished executing its code and has a final result, the scope of the coroutine (viewModelScope), will run the remaining code in the Ui thread again. In this case, it just sets the value to the LiveData object to “notify” the View that the data is ready. In case there has been some error or no data has been found, the LiveData object will contain a different value or state. Letting know it to the view what happened.

View: as you already know, the View is the one inflating the interface, getting the events from the user and triggering the correspondent call, to then “paint” the results. But in this case it will do it in a slightly different way. Instead of having functions to do the “painting” actions it will observe the LiveData objects inside the ViewModel and it will change the view depending on that. So, no more exposed functions in a view, waiting to be called. The relation with the internal data is more direct now, in the same way it’s more direct as we have discussed the LiveData is aware of the view lifecycle.

PS: I have a small project set with all the basis of this post. (Don’t pay attention to the interface). But I think it could be useful to better undertand the whole flow in real action!

--

--