Network calls and database access must never run on the main (UI) thread — doing so freezes the UI and can trigger an ANR (Application Not Responding) crash. Coroutines are Kotlin’s modern approach to asynchronous code, replacing older callback-heavy or AsyncTask-based patterns.

class UserViewModel(private val repository: UserRepository) : ViewModel() {
    private val _user = MutableLiveData<User>()
    val user: LiveData<User> = _user

    fun loadUser(id: Int) {
        viewModelScope.launch {
            val result = repository.getUser(id)   // suspend function — runs off the main thread
            _user.value = result
        }
    }
}

viewModelScope is a coroutine scope tied to the ViewModel’s lifecycle — it’s automatically cancelled when the ViewModel is cleared, so you never need to manually cancel network calls when the user navigates away.

A suspend function can pause and resume without blocking the calling thread. Under the hood, Retrofit and Room both generate suspend functions for you, so calling them from a coroutine “just works” without extra threading code.