Hi all, from the doc `*Warning:* Never collect a f...
# coroutines
b
Hi all, from the doc
*Warning:* Never collect a flow from the UI directly from *launch* or the *launchIn* extension function if the UI needs to be updated. These functions process events even when the view is not visible. This behavior can lead to app crashes. To avoid that, use the *repeatOnLifecycle* API as shown above.
What is the safest way to collect stateFlow in compose since we don’t have access to `*`repeatOnLifecycle`*` since it is not recommended to collect using
launch
?
Also prob better to put in #compose
i
collectAsState()
is also totally unaware of Lifecycle changes (it will continue to collect after you hit the home button, for instance). You'll want
flowWithLifecycle
if you want to be Lifecycle aware: https://medium.com/androiddevelopers/a-safer-way-to-collect-flows-from-android-uis-23080b1f8bda#1b7b
b
Unfortunately, flowWithLifecycle is more about "saving resources" and not about lifecycle awareness, because it only controls the upstream, and not the downstream. And since it uses channel under the hood you still may collect 64 items even if lifecycle state is below minState
That means, you still have to wrap it with repeatOnLifecycle OR move your collect code to onEach above flowWithLifecycle
a
what are you looking to do/avoid by doing that in this context?
b
i want to listen when new value has been emitted. I think the best option is to do it on the ViewModel and on compose to
collectAsState()
b
what are you looking to do/avoid by doing that in this context?
basically, I want a lifecycle-aware flow collection 🙂 Here is an example. In Fragment.onCreate you're subscribing to some flow and update UI (provided via lifecycle-aware ViewBinding delegate that throws an error when attempting to access it if the view is destroyed). That means the following example
Copy code
(0..1000).asFlow()
    .flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
    .collect { counter ->
        binding.textView.text = it
        delay(1000) // not a real world example. In a real-world example some suspend animation may be here. Also delay/animation is not necessary (because channel flow may do re-dispatches and won't consume all the buffer at once), it just makes the problem easier to repro. 
    }
will crash the app. You may suggest a few things here 1. binding delegate shouldn't crash / we should use nullable binding etc. But it's the only indication that we are using lifecycle unaware api. 2. I need to use onViewCreated + viewLifecycleOwner.lifecycle (because it will cancel the coroutine once view is destroyed). But it is not always possible, and it also doesn't save you from manipulating your view (aka playing some animation) when fragment is invisible. The main problem I see here is
flowWithLifecycle
is supposed (according to the docs/blogpost) to be fully equivalent to repeatOnLifecycle + collect:
Copy code
repeatOnLifecycle(..., STARTED) {
   flow.collect { 
      // some ops with UI
   }
}
IS NOT EQUIVALENT TO
flow.flowWithLifecycle(..., STARTED)
    .collect {
       // some ops with UI
    }
But this is not true as I said before. What will be a full equivalent is
collectWithLifecycle
a
if this matters to your code when you're doing
.collectAsState
with it in compose then yes, see your (1). 🙂 recomposition can happen at any time independent of any kind of flow emits into a single snapshot state object, so if you have something downstream of it that's taking lifecycle-sensitive actions, that code is already in trouble for other reasons