Every coroutine scope question becomes easy once you stop asking what it starts and start asking what cancels it.
Scopes are defined by their death
viewModelScopedies when the ViewModel clearslifecycleScopedies with the lifecycle owner- A scope you built with
CoroutineScope(...)dies when you remember to cancel it
That last one is where the leaks are.
The question to ask
Whatever work you are starting, ask what should stop it. A network call for a screen should stop when the screen goes. A file upload the user asked for probably should not.
// dies with the screen — right for a screen's data
viewModelScope.launch { repo.refresh() }
// outlives the screen — right for work the user committed to
applicationScope.launch { uploader.send(file) }
supervisorScope is about siblings
In a plain coroutineScope, one failing child cancels the rest. In a
supervisorScope, the others carry on. Loading three independent sections of a
screen wants supervision; three steps of one operation does not.
Collect with the lifecycle
collectAsStateWithLifecycle instead of collectAsState. Otherwise the flow
keeps collecting behind a backgrounded screen, which is a battery bug that never
shows up in testing.