brabo-hi
02/02/2022, 5:40 PM*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
?Trevor Stone
02/02/2022, 5:41 PMIan Lake
02/02/2022, 5:50 PMcollectAsState()
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#1b7bbezrukov
02/02/2022, 8:28 PMbezrukov
02/02/2022, 8:31 PMAdam Powell
02/02/2022, 9:20 PMbrabo-hi
02/02/2022, 9:52 PMcollectAsState()
bezrukov
02/03/2022, 9:07 AMwhat 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
(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:
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
Adam Powell
02/03/2022, 3:40 PM.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