Technical blog from Craig Russell.
This post describes how to use Coroutines
in ViewModels
, making use of the new ViewModelScope
extension property. This allows coroutines to be cancelled automatically when the ViewModel
is being cleared.
As of lifecycle 2.1.0-alpha01, there is now easy support for coroutines in ViewModels
, using an extension property called ViewModel.viewModelScope
.
viewModelScope
is likely best explained by starting with an example. Let’s imagine we are following the standard Android Architecture Components setup, and we have a ViewModel
which extends androidx.lifecycle.ViewModel
If you wanted to make use of coroutines from your ViewModel
, you might currently do something like this:
class LoginViewModel : ViewModel(), CoroutineScope {
// Define job and coroutine context
private val job = SupervisorJob()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// cancel the job when the Activity is being destroyed for good
override fun onCleared() {
super.onCleared()
job.cancel()
}
fun onUserDoingAThing() {
launch {
// get your work done
}
}
}
By doing this, you would be able to use launch{ }
inside the ViewModel
to run coroutines.
By overriding the onCleared()
function, you can cancel the coroutine job to ensure you aren’t continuing to do work that is no longer necessary.
Hopefully this looks fairly familiar to you, and you largely understand why all the code is needed.
Now, let’s look at the new way of doing it and let’s see what code we can remove while keeping the same functionality.
class LoginViewModel : ViewModel() {
fun onUserDoingAThing() {
viewModelScope.launch {
// get your work done
}
}
}
Wait… where did all the code go? It just isn’t needed! 🎉
CoroutineScope
It’s no longer required that you specify that your ViewModel
implements CoroutineScope
. You can use viewModelScope
which is already available inside your ViewModel
.
Any time you want to launch a coroutine, therefore, you can use viewModelScope.launch { }
.
Note, by default it will run the coroutine on Dispatchers.Main
, but you can override that as normal to use any dispatcher. For example, viewModelScope.launch(Dispatchers.IO) { }
.
Job
You no longer have to create a job to use for cancellation. The latest ViewModel
already comes equipped under the hood with a SupervisorJob
which is likely what you want for this scenario anyway.
Use of a SupervisorJob
means that you can launch multiple coroutines and have it so that one failing will not affect any of the others.
A failure or cancellation of a child does not cause the supervisor job to fail and does not affect its other children
onCleared()
function and cancel the jobJob cancellation now happens automatically when the onCleared()
function is called.
ℹ️ As a reminder, this is not called when the Activity
is being destroyed during a configuration change, such as the user rotating their device.
However, it is called when the Activity
is being destroyed for good, such as when the user hits the back button to navigate away from the Activity
.
viewModelScope
was added in 2.1.0-alpha01
of the lifecycle extensions dependency, so make sure you have at least that version in your build.gradle
file, and make sure your project is configured for using coroutines.
For example:
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0-alpha02"
implementation "androidx.lifecycle:lifecycle-extensions:2.1.0-alpha02"
annotationProcessor "androidx.lifecycle:lifecycle-compiler:2.1.0-alpha02"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1'
You can now use coroutines in your ViewModels
with the minimum of effort. The Architecture Components libraries will now give you a very sensible default setup without writing any code at all.
Note, if your work isn’t tied to a particular screen and should continue even if the user navigates away, this isn’t what you should be using; you should be using something with a lifecycle that isn’t tied to the screen.
Coroutines that are tied to a particular screen, however, can now be launched easily, and cancelled automatically when the screen is going away.
AndroidX Lifecycle Release Notes
The original article was ahead of its time in suggesting a SupervisorJob
was used under the hood; at the time of writing it was using Job
; thanks to @fabioCollini and @geoffreymetais for correcting me.
Since publishing, it has now changed to use the SupervisorJob
which always felt like the best choice. Thanks to @sindrenm for letting me know they’d updated it.