Should we unregister Flow collection in Fragment/Activity?

Viewed 606

In the Android official documentation, it says the following about StateFlow vs Livedata:

LiveData.observe() automatically unregisters the consumer when the view goes to the STOPPED state, whereas collecting from a StateFlow or any other flow does not.

and they recommend to cancel eacy flow collection like this:

// Coroutine listening for UI states
    private var uiStateJob: Job? = null

    override fun onStart() {
        super.onStart()
        // Start collecting when the View is visible
        uiStateJob = lifecycleScope.launch {
            latestNewsViewModel.uiState.collect { uiState -> ... }
        }
    }

    override fun onStop() {
        // Stop collecting when the View goes to the background
        uiStateJob?.cancel()
        super.onStop()
    }

As far as I know about structured concurrency, when we cancel the parent job, all children jobs are cancelled automatically and flow terminal operator collect should not be an exception. In this case, we are using lifecycleScope that should cancel when the lifecycleOwner is destroyed. Then, why do we need to manually cancel flow collection in this case? what am I missing here?

2 Answers

When using lifecycleScope you do not need to cancel the jobs or scope yourself, as this scope is bound to the LifecycleOwner's lifecycle and gets cancelled when the lifecycle is detroyed. The flow is then cancelled too.

It changes as soon you are using your own CoroutineScope:

val coroutineScope = CoroutineScope(Dispatchers.Main)

override fun onCreate() {
  // ...
  coroutineScope.launch {
    latestNewsViewModel.uiState.collect { uiState -> ... }
  }
}

override fun onDestroy() {
  // ...
  coroutineScope.coroutineContext.cancelChildren()
}

In that case you would need to take care of cancelling the scope (or job's) yourself.

According to @ChristianB response, we don't need to cancel coroutines that are attached to a lifecycle. the collect() is running inside a coroutine and will cancel too, when coroutines cancels.

Then, why do we need to manually cancel flow collection in this case? what am I missing here?

When we want to use coroutines with a custom lifecycle (not using fragment/activity lifecycle). For example, I've created a helper class that needed to do sth inside itself. It launches a coroutine and it was managing (cancel/join...) its job.

Related