Coroutines on Android (Structured Concurrency)
viewModelScope & Structured Concurrency
Long-running work (network, disk) must run off the main thread. `viewModelScope` launches coroutines that are automatically cancelled when the `ViewModel` is cleared โ no leaks, no callbacks.
๐ Explanation
`viewModelScope` is a `CoroutineScope` tied to the ViewModel; coroutines launched in it are cancelled automatically in `onCleared()`. `async` starts concurrent work and returns a `Deferred` you `await`. Under structured concurrency, if one child coroutine throws, its siblings are cancelled and the exception propagates to the `try/catch` โ preventing orphaned work and resource leaks.
Note: Code execution is not available for Android/Kotlin in the browser. Use Android Studio to run and test the app.
import androidx.lifecycle.ViewModelimport androidx.lifecycle.viewModelScopeimport kotlinx.coroutines.asyncimport kotlinx.coroutines.flow.MutableStateFlowimport kotlinx.coroutines.flow.updateimport kotlinx.coroutines.launchclass DashboardViewModel(private val repo: DashboardRepository) : ViewModel() {private val _state = MutableStateFlow(DashboardState())val state = _state // collected as StateFlow in the UIfun refresh() {// viewModelScope is tied to the ViewModel lifecycle.// When the ViewModel is cleared, this coroutine is cancelled.viewModelScope.launch {_state.update { it.copy(isLoading = true, error = null) }try {// Run two suspend calls CONCURRENTLY, then await both.val profile = async { repo.fetchProfile() }val feed = async { repo.fetchFeed() }_state.update {it.copy(isLoading = false,profile = profile.await(),feed = feed.await())}} catch (e: Exception) {// If one child fails, structured concurrency cancels the sibling._state.update { it.copy(isLoading = false, error = e.message) }}}}}data class DashboardState(val isLoading: Boolean = false,val profile: Profile? = null,val feed: List<Post> = emptyList(),val error: String? = null)