1. Overview
In this article, we will learn the differences between ViewModelScope vs LifeCycleScope.
Kotlin coroutines provide an API that enables you to write asynchronous code. With Kotlin coroutines, you can define a CoroutineScope
, which helps you to manage when your coroutines should run. Each asynchronous operation runs within a particular scope.
Lifecycle-aware components provide first-class support for coroutines for logical scopes in your app, along with an interoperability layer with LiveData
.
2. Android ViewModelScope
A ViewModelScope is defined for each ViewModel
in your app. You can access the CoroutineScope
of a ViewModel
through the viewModelScope
property of the ViewModel. Thus, you can use this scope directly in your ViewModel
for launching coroutines instead of creating a new coroutine scope. This ViewModelScope is tied to the ViewModel
.
You must add the following dependency in your project:
androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0
Any coroutine launched in this scope is automatically canceled if the ViewModel
is cleared. You can use this scope when you want the task to execute only if the ViewModel
is active.
Basically, in the MVVM pattern we use ViewModel
manages the data and tied to a particular Activity/Fragment
. So once that Activity/Fragment
gets destroyed, its corresponding ViewModel
also cleared. Thus, it cancels all incomplete jobs started by the viewModelScope
, throwing CancellationException
.
Look at the following code. The getHeadlines
method uses the viewModelScope
to launch the coroutine that would execute a suspend function headlinesDataSource.getAllUSHeadlines(map)
. The viewModelScope
is available for use automatically in your view model.
@HiltViewModel open class HeadlinesViewModel @Inject constructor(private val headlinesDataSource: HeadlinesDataSource): ViewModel() { private val _res = MutableLiveData<Resource<HeadlinesResponse>>() val res : LiveData<Resource<HeadlinesResponse>> get() = _res init { getHeadlines() } fun getHeadlines() = viewModelScope.launch { _res.postValue(Resource.loading(null)) val map = HashMap<String, String>() map["country"] = AppConstants.COUNTRY map["category"] = AppConstants.BUSINESS map["apiKey"] = BuildConfig.API_KEY headlinesDataSource.getAllUSHeadlines(map).let { resp -> _res.postValue(Resource.success(resp)) } } }
Assume the suspend function is still in execution. Now, user presses the back button causing the activity or fragment to destroy. Once it is destroyed, the view model is also cleared. Android then would also cancel the coroutines executing within the view model scope.
3. LifeCycleScope
A LifecycleScope
is defined for each Lifecycle
object. A Lifecycle is a class that holds the information about the lifecycle state of a component (like an activity or a fragment) and allows other objects to observe this state.
Android cancels any coroutine launched in this scope when the corresponding Lifecycle
is destroyed. You can access the CoroutineScope
of the Lifecycle
either via lifecycle.coroutineScope
or lifecycleOwner.lifecycleScope
properties. A lifecycle owner is a class that has an Android lifecycle.
For example, the fragment itself is the LifecycleOwner
. The below fragment uses the lifecycleOwner.lifecycleScope
to create precomputed text asynchronously:
class MyFragment: Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewLifecycleOwner.lifecycleScope.launch { val params = TextViewCompat.getTextMetricsParams(textView) val precomputedText = withContext(Dispatchers.Default) { PrecomputedTextCompat.create(longTextContent, params) } TextViewCompat.setPrecomputedText(textView, precomputedText) } } }
4. Conclusion
To sum up, we have learned the differences between ViewModelScope vs LifeCycleScope.