Different Types of Scopes in Kotlin Coroutines | by Ahmed Ally | May, 2025

Coroutine scopes are a crucial concept in Kotlin coroutines as they define the lifetime and cancellation behavior of coroutines. Let me provide a more detailed explanation of the different types of scopes you can use in Android development.

This is the basic scope interface that you can implement or instantiate directly:

// Creating a custom scope
val customScope = CoroutineScope(Dispatchers.IO + SupervisorJob())

// Using the scope
customScope.launch {
// Coroutine code
}

// Cancelling all coroutines in this scope
customScope.cancel()

GlobalScope is a singleton scope that lives for the entire duration of the application:

GlobalScope.launch {
// This coroutine is not tied to any specific lifecycle
// and will continue until completion or application shutdown
}

⚠️ Warning: Using GlobalScope is generally discouraged in Android because:

  • It can lead to memory leaks
  • Coroutines continue running even if components are destroyed
  • It makes testing more difficult

a. viewModelScope

Tied to a ViewModel’s lifecycle, automatically cancelled when the ViewModel is cleared:

class MyViewModel : ViewModel() {
fun loadData() {
viewModelScope.launch {
// This coroutine is cancelled when ViewModel is cleared
val result = repository.fetchData()
_data.value = result
}
}
}

b. lifecycleScope

Tied to an Activity or Fragment’s lifecycle, automatically cancelled when the lifecycle is destroyed:

class MyFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

lifecycleScope.launch {
// This coroutine is cancelled when the Fragment is destroyed
repository.getData().collect { data ->
updateUI(data)
}
}
}
}

c. viewLifecycleOwner.lifecycleScope

Specifically for Fragments, tied to the Fragment’s view lifecycle:

// In a Fragment
viewLifecycleOwner.lifecycleScope.launch {
// This coroutine is cancelled when the Fragment's view is destroyed
// Important for operations related to views in a Fragment
}

a. coroutineScope

Creates a new scope that inherits the parent context and waits for all child coroutines to complete:

suspend fun fetchDataParallel() = coroutineScope {
val data1 = async { api.fetchFirstData() }
val data2 = async { api.fetchSecondData() }

// coroutineScope waits for both async blocks to complete
// before returning the result
CombinedResult(data1.await(), data2.await())
}

b. supervisorScope

Similar to coroutineScope, but uses a SupervisorJob so failures in one child don’t affect others:

suspend fun fetchMultipleResources() = supervisorScope {
// These operations run independently - if one fails, others continue
val users = launch { repository.fetchUsers() }
val posts = launch { repository.fetchPosts() }
val settings = launch { repository.fetchSettings() }

// Wait for all to complete
users.join()
posts.join()
settings.join()
}

The Android-specific scopes are tied to lifecycle states:

// Runs when the lifecycle is at least in STARTED state
lifecycleScope.launchWhenStarted {
// This coroutine is suspended when the lifecycle goes below STARTED
// and resumes when it returns to STARTED or above
}

// Runs when the lifecycle is at least in RESUMED state
lifecycleScope.launchWhenResumed {
// Only runs in RESUMED state
}

// Runs when the lifecycle is at least in CREATED state
lifecycleScope.launchWhenCreated {
// Runs in CREATED, STARTED, or RESUMED states
}

  • viewModelScope: Use for data operations that should be tied to a ViewModel’s lifecycle
  • lifecycleScope: Use for UI-related operations in Activities/Fragments
  • viewLifecycleOwner.lifecycleScope: Use for view-related operations in Fragments
  • Custom CoroutineScope: Use for operations that need a specific lifetime or context
  • coroutineScope/supervisorScope: Use within suspending functions for structured concurrency

Is there a specific scope type you’d like me to explore in more detail?

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.