LiveDataScope vs ViewModelScope in Android
Note: This might be late answer for this topic if Author of OP already has understanding about this, But providing some pointers for the referencing comment of @IgorGanapolsky.
Let's see what is the main difference between viewModelScope & LiveDataScope
1. viewModelScope:
Official doc says that,
CoroutineScope
tied to thisViewModel
. This scope will be canceled whenViewModel
will be cleared, i.eViewModel.onCleared
is called
Meaning that coroutine scope is tied to ViewModel, and once ViewModel gets cleared this scope gets destroyed by cancelling all child coroutine jobs.
Basically, in MVVM pattern we use ViewModel
tied to a particular Activity/Fragment
. So once that Activity/Fragment
gets destroyed, its ViewModel
reaches a cleared state. Thus, it cancels all incomplete jobs started by viewModelScope
, throwing CancellationException
.
So a usecase of viewModelScope
is: inside ViewModel
when you've got any suspended function to be called and need a CoroutineScope
, inspite of making new one you can directly use this one out of the box from viewodel-ktx library.
class SomeViewModel: ViewModel() {
fun someFunction() {
viewModelScope.launch {
callingSomeSuspendedFun()
callingAnotherSuspendedFun()
}
}
}
Note that you don't need to explicitly override onCleared()
method of ViewModel
to cancel the scope, it does automatically for you, cheers!
2. LiveDataScope:
Now speaking of LiveDataScope
, it's actually an interface provided to build better support for LiveData/CoroutineLiveData
that can have CoroutineScope
out of the box! use livedata-ktx version
Now imagine a situation that you're having a MVVM pattern and wanted to return LiveData
from repository to view model. your repository also contains some suspended functions and some coroutine scope.
In that situation when you do some suspended method calls & return the result as live data, there would be some extra work. you'll need transform your data to particular live data after getting it as result. see the example below:
class SomeRepository {
suspended fun someApiCall() : LiveData<Result> {
val result = MutableLiveData<Result>()
someCoroutineScope.launch {
val someData = someOtherCallToGetResult()
result.postValue(someData)
}
return result
}
}
Imagine you had to write above code block due to LiveData
didn't had any support for Coroutines ... but until now!
Now you can directly use liveData { }
function that returns you LiveData
object giving you scope of LiveDataScope
in such a way that you can continue your suspended work and emit the result at the same level rather than getting it messy way like above. So above code block can now optimized by following code or better:
class SomeRepository {
suspended fun someApiCall() : LiveData<Result> {
return liveData<Result> {
val someData = someOtherCallToGetResult()
emit(someData)
}
}
}
So use case of liveData would be at repository level when using MVVM pattern if you expose LiveData to viewmodel from respository rather than creating new inside viewmodel. Please note that there's no thumb rule about liveData
method shouldn't be used at viewmodel directly. You can if you want to avoid viewModelScope
completely.
TL;DR
Check out the liveData method,
Doc states that, The
liveData
building block serves as a structured concurrency primitive between coroutines andLiveData
. The code block starts executing whenLiveData
becomes active and is automatically canceled after a configurable timeout when theLiveData
becomes inactive. If it is canceled before completion, it is restarted if theLiveData
becomes active again. If it completed successfully in a previous run, it doesn't restart. Note that it is restarted only if canceled automatically. If the block is canceled for any other reason (e.g. throwing aCancelationException
), it is not restarted.
I hope that make sense!
The names imply what they actually are:
A ViewModelScope is defined for each ViewModel in your app. Any coroutine launched in this scope is automatically canceled if the ViewModel is cleared.
This means that you can do some tasks(like continuous processing) in a coroutine that is in the scope of the ViewModel. The advantage is that you don't have to care anymore when the ViewModel will be stopped to stop your coroutine (this is a big pain when working with global things like java threads). The lifecycle of the ViewModel is related to when an activity is ended.
The LiveDataScope
is used for emitting values in the scope of a LiveData
object. This means that as long as the LiveData
object is alive and there are subscribers that coroutine will work, however once all the subscribers are out the coroutine will stop. This coroutine also restarts once the LiveData
is active again.
Basically these are 2 coroutine contexts each responsible for the lifecycle of its element.
PS:
It sounds like ViewModelScope takes care of lifecycle automatically and you can do network request in the block.
First of all, network requests cannot be done from the Main thread, you usually do them from IO scope, you can read more here. The second thing is that you should take a look at the lifecycle of the ViewModel compared to Activity if you want to understand why LiveDataScope
is usually combined with ViewModelScope
, you can read about that here.
The short answer to your question is that you cannot be sure that the view is created from the ViewModelScope
so if you want to push some updates to UI you should push them as long as someone is subscribed to LiveData, this is where the LiveDataScope
comes into play.