Android ViewModel+LiveData+Coroutines contraption


I’ve been playing around with ViewModel, LiveData and lastly Coroutines, now that the latest are stable in Kotlin 1.3.
I believe and hope I came up with a setup that allows clean, (somehow) isolated and concise code. Some might want to split things up even more (and nothing in here forbids it). I was asked to write about this and share it, so I could explain it and get more in-depth feedback about what I’m trying to accomplish:
Use Kotlin to simplify my code by being more concise and focus on (isolated) minimal tasks.
Setting up the table
I’ll first use a simple ViewModel
with some LiveData
objects that the Activity
will be able to observe. Here is how MainActivity
uses it:
I’m also okay in most cases with Google’s demo code on ViewModel
:
getUsers()
gets the MutableLiveData
as a LiveData
and if it was not initialized yet, triggers a loading that will be notified later on to the Observer
using postValue()
. This loading work is supposed to be intensive (network or database related) and should be done asynchronously.
Warming up ingredients
I’ll now make use of an in-memory Room database for the data:
Without Coroutines nor any other threading mechanism, I placed allowMainThreadQueries()
. This is for the purpose of this paper and won’t last.
Also using a brief DataManager
:
This is where the logic around and between different data sources might take place.
Sharping the knifes
I then made use of Coroutines, and marked my methods as @UiThread
or @WorkerThread
:
GlobalScope.launch
sets a scope to work on. Since the actual work will be in background, I’ll use the Main
(UI thread) to dispatch result. async
is used on an IO
thread, and that’s where the heavy work gets done. The await()
makes the magic happens, as usersFromDb
will be set only when async
returns.
This is all making it as if the heavy work was synchronous.
Here is the beauty of Coroutines: sequential yet asynchronous work.
Finally, I made a Coroutines wrapper to better suit my needs (this is pure syntactic sugar and is totally optional):
This makes the CoroutineContext
easier to work with, at least for me. And also makes await()
implicit.
Diner is served
I wanted a DataManager
that does not care about Coroutines:
I wanted simplified Coroutines calls:
And I wanted to keep the simple ViewModel
usage from an Activity
.
Savor time
MainActivity
's code hasn’t changed. The logic around the ViewModel
is the same. The ViewModel
just calls Coroutines and sets the result. The DataManager
is where the heavy work happens, yet it is written without any knowledge of Coroutines.
What is done under the hood is no concern for other layers of code. The resulting code seems sequential, concise, and actually splits responsibilities.
I’m hoping this have introduced to you a practical example of Coroutines usage without your Android applications. Moreover, I hope this will give my fellow Android developers friends new ideas around ViewModel, LiveData and Coroutines. Please review, debate, comment and share.
Code is available as a sample yet complete Github project under GNU GPL 3: https://github.com/shkschneider/android_viewmodel_livedata_coroutines (I urge you to explore the history first, as I made this all step by step.)
The sample application exposes three type of data (User, Project, Notification) and display data as pretty formatted Gson.