In Android development, coroutines are commonly launched from ViewModel classes using viewModelScope. The typical usage looks like this:
viewModelScope.launch {
// ... implementation
}In Android development, coroutines are commonly launched from ViewModel classes using viewModelScope. The typical usage looks like this:
viewModelScope.launch {
// … implementation
}By default, if a dispatcher is not specified, any coroutine launched in viewModelScope will run on the main thread. However, in most use cases, we launch these coroutines to perform background tasks, such as — making API calls, or database operations. To ensure these tasks run in the background thread, it is essential to specify an appropriate dispatcher:
- I/O tasks should use Dispatchers.IO
- CPU-intensive tasks should use Dispatchers.Default
- UI updates should use Dispatchers.Main
For example, to run an I/O task, we can specify the dispatcher like this:
viewModelScope.launch(Dispatchers.IO) {
// ...implementation
}Optimizing Coroutine Launching
To streamline coroutine launching, we can create extension functions on ViewModel that automatically use the specified dispatcher by default.
Step 1: Create Extension Functions
We can define extension functions for different dispatchers:
kotlin
fun ViewModel.launchIO(block: suspend CoroutineScope.() -> Unit) {
viewModelScope.launch(Dispatchers.IO, block = block)
}
fun ViewModel.launchDefault(block: suspend CoroutineScope.() -> Unit) {
viewModelScope.launch(Dispatchers.Default, block = block)
}
fun ViewModel.launchMain(block: suspend CoroutineScope.() -> Unit) {
viewModelScope.launch(Dispatchers.Main, block = block)
}
Step 2: Use the Extension Functions
You can now use these extension functions in your ViewModel as follows:
class MyViewModel : ViewModel() {
fun fetchData() {
launchIO {
// … implementation
}
}
}
Benefits
- Consistency: These extension functions maintain consistent coroutine usage for specific tasks.
- Readability: The function names clearly indicate their purpose, making the code easier to understand.
- Maintainability: Centralizing dispatcher management simplifies updates and modifications over time.
Optional: Single Extension Function with Overloading
As an alternative, you can create a single extension function that accepts a dispatcher as a parameter. By default, this function can run on the I/O dispatcher:
fun ViewModel.launchOn(dispatcher: CoroutineDispatcher = Dispatchers.IO, block: suspend CoroutineScope.() -> Unit) {
viewModelScope.launch(dispatcher, block = block)
}Example Usage
We can then use this overloaded function for different dispatchers:
class MyViewModel : ViewModel() {
fun someDatabaseOperation() {
launchOn(Dispatchers.IO) {
// … implementation
}
}
fun someUiUpdates() {
launchOn(Dispatchers.Main) {
// … implementation
}
}
fun someCPUIntensiveTask() {
launchOn(Dispatchers.Default) {
// … implementation
}
}
}Conclusion
Implementing a structured approach to launching coroutines in your ViewModel helps improve code quality, maintainability, and readability. By utilizing extension functions or a flexible overloaded function, you can ensure that your coroutines run on the appropriate dispatcher, aligning with best practices in Android development.
Thank you for reading. Please share feedback!
Coroutines and Dispatchers was originally published in MindOrks on Medium, where people are continuing the conversation by highlighting and responding to this story.