DEV Community

Cover image for Scopes in Kotlin: Controlling Lifecycle and Cancellation of Coroutines
Alan Gomes for Comunidade Dev Space

Posted on

Scopes in Kotlin: Controlling Lifecycle and Cancellation of Coroutines

1 – Introduction

Managing the lifecycle of coroutines is one of the most important aspects of asynchronous programming in Kotlin. To this end, Kotlin provides scopes: structures that help you control how and when coroutines run, as well as making it easy to cancel tasks when necessary.

In this article, we'll explore the main types of scopes in Kotlin and their differences. You'll see how choosing the right scope can simplify managing coroutines in your code.


2 – What are Scopes?

Scopes in Kotlin are "managers" of the coroutines lifecycle. They:

  1. Determine where and how coroutines will be executed.
  2. Control the life cycle of coroutines, including their cancellation.

Scope Types:

  1. Class-based scopes: Used for long-term operations or associated with specific components, such as viewModelScope in Android.
  2. Role-based scopes: Used for temporary and local operations, such as coroutineScope and supervisorScope.

3 – Class-Based Scopes

3.1 – CoroutineScope

  • What is it? It is the base interface for creating custom scopes. You can associate a CoroutineScope with a specific context (e.g. a Dispatcher) to manage coroutines.
  • When to use? To create custom scopes that control the lifecycle of multiple coroutines.
  • Example:
import kotlinx.coroutines.*

fun main() {
    val customScope = CoroutineScope(Dispatchers.Default)

    customScope.launch {
        println("Running inside CoroutineScope:${Thread.currentThread().name}")
    }
}
Enter fullscreen mode Exit fullscreen mode

3.2 – viewModelScope

  • What is it? A special scope provided by the Android Jetpack library, designed for the lifecycle of ViewModels. When the ViewModel is destroyed, all coroutines associated with the viewModelScope are automatically canceled.
  • When to use?
    To manage UI-associated coroutines on Android.

  • Example:

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch

class MyViewModel : ViewModel() {
    fun loadData() {
        viewModelScope.launch {
            println("Loading data into thread: ${Thread.currentThread().name}")
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

3.3 – backgroundScope

  • What is it? A scope designed for long-term operations that can continue even if the life cycle of the main component ends.
  • When to use?
    For tasks that need to persist beyond their immediate lifecycle, such as background sync.

  • Example:

import kotlinx.coroutines.*

fun main() {
    val backgroundScope = CoroutineScope(Dispatchers.IO)

    backgroundScope.launch {
        println("Running in the background:${Thread.currentThread().name}")
    }
}
Enter fullscreen mode Exit fullscreen mode

4 – Role-Based Scopes

4.1 – coroutineScope

  • What is it? A suspend function that creates a temporary scope. All coroutines within a coroutineScope share the same Job.
  • When to use? To ensure that all tasks within a block are completed or cancelled together.
  • Example:
import kotlinx.coroutines .*

suspend fun main() {
    coroutineScope {
        launch {
            println("Coroutine 1 running...")
        }
        launch {
            println("Coroutine 2 running...")
        }
    }
    println("All coroutines completed.")
}
Enter fullscreen mode Exit fullscreen mode

4.2 – supervisorScope

  • What is it? Similar to coroutineScope, but with one crucial difference: failures in one coroutine do not affect the others within the scope.
  • When to use? To ensure that all tasks within a block are completed or cancelled together.
  • Example:
import kotlinx.coroutines.*

suspend fun main() {
    supervisorScope {
        launch {
            println("Coroutine 1 running...")
            throw RuntimeException("Error in Coroutine 1")
        }
        launch {
            println("Coroutine 2 running...")
        }
    }
    println("SupervisorScope completed.")
}
Enter fullscreen mode Exit fullscreen mode

5 – Comparison: coroutineScope vs. supervisorScope

Appearance coroutineScope supervisorScope
Cancellation Cancels all coroutines on failure. Only the coroutine with the error is canceled.
Relationship between tasks Coroutines dependent on each other. Independent coroutines .
When to use When all tasks must succeed. When tasks can fail independently.

6 – Conclusion

Scopes are powerful tools in Kotlin for managing the lifecycle of coroutines. They help prevent resource leaks and make it easier to cancel unnecessary tasks.

Scope Summary:

  1. CoroutineScope: For custom scopes.
  2. viewModelScope: To manage coroutines bound to ViewModels in Android.
  3. coroutineScope: For temporary scopes where all tasks depend on each other.
  4. supervisorScope: For independent tasks. In the next article, we will explore utility methods and execution methods that you can use to further optimize your coroutines .

Reference
Official Kotlin documentation on coroutines

Top comments (0)