Are there any benefits LiveData still has over Flo...
# coroutines
f
Are there any benefits LiveData still has over Flow? Or can Flow do anything LiveData can do?
👍 1
c
The main reason to use LiveData over Flow is its integration with the Lifecycle (or if you have to use it from Java). But by collecting a Flow in a
lifecycleScope
you pretty much get the same benefit with all the additional features and much nicer API of Flow. Personally, I don’t see any reason now to choose LiveData over Flow (especially StateFlow)
i
With
StateFlow
and
SharedFlow
, there's no reason to purposefully choose LiveData for any new code you're writing
😍 2
☝️ 3
💪 3
😲 2
K 16
f
Thank you. But Flow doesn't cache the value across orientation changes, right? If I return a Flow from a Room query and collect in in the lifecycleScope of a fragment, rotating the device would requery the database. Is that correct?
z
hold on a second, what is the replacement for
SavedStateHandle.getLiveData
then? Also, does this mean one has to reimplement Paging 3's
cachedIn(viewModelScope)
or is there a replacement for it?
👍 4
r
LiveData is built to be lifecycle aware data holder
i
shareIn
in your ViewModel is what you'd use to cache state across config changes
👍 3
cachedIn
is just Paging 3's equivalent to
shareIn
before
shareIn
was a thing - expect for it to be unnecessary/removed in the future
SavedStateHandle
doesn't depend on coroutines (so it can't provide a
StateFlow
directly), but you can always
asFlow
anything that gives you a
LiveData
f
that's interesting, thank you
is it planned to add
SavedStateHandke#getStateFlow
?
i
SavedStateHandle
 doesn't depend on coroutines (so it can't provide a 
StateFlow
 directly)
j
1) databinding. support for state flow is not there yet. 2) launch when started just suspend, doesn't stop collection of the flow.
f
thank you everyone
z
Oh nice, thanks! ☺️
f
one downside of Flow that's left is that the syntax is quite verbose as compared to observing a LiveData
🤔 1
i
You can certainly write your own extension such as
Copy code
/**
 * Similar to [kotlinx.coroutines.flow.launchIn] but using
 * [androidx.lifecycle.LifecycleCoroutineScope.launchWhenStarted].
 */
fun <T> Flow<T>.launchWhenStartedIn(
        lifecycleOwner: LifecycleOwner
) = lifecycleOwner.lifecycleScope.launchWhenStarted {
    collect()
}
which then lets you use a more LiveData like syntax of
Copy code
viewModel.yourFlow.onEach { items ->
  // Do something with the items
}.launchWhenStartedIn(viewLifecycleOwner)
K 1
🙏 3
❤️ 6
d
Funny that isn't provided in Android ktx libs...
t
After seeing this opinion, I have refrained from using StateFlow. What do you think about this? I'm worried that StateFlow will be overkill. https://twitter.com/JakeWharton/status/1260545289389322241
i
Yep, in most cases you want a Flow (i.e., from
flow
or
callbackFlow
) or a
SharedFlow
(which replaces
ConflatedBroadcastChannel
, etc.)
The type hierarchy is really nice in Flow, from the core primitive of a stream of events (
Flow
) to Flow+replay (
SharedFlow
which extends
Flow
) and finally Flow+replay+always available current state (
StateFlow
which extends
SharedFlow
)
LiveData
has none of those lower level constructs - it is solely a take it or leave it type of API (perfect for the 80% case, terrible for the rest). Anti-patterns like
SingleLiveEvent
are just a sign that you're actually looking for one of those lower level APIs and you can avoid all that mess in the Flow world
☝️ 2
👍 6
g
1. Databinding support is hopefully coming. It would be indeed great, wait when we will be able stop using livedata in databinding viewmodels 2. If such behavior is required, it's not hard to achieve the same with flow, subscribe on every onStart and cancel it onStop, can be done as an extension function similar to launchOn with lifecycle instead of scope as argument
n
So we can basically exchange LiveData with StateFlow which is also similar to BehaviorSubject in rx. But what to use for SingleLiveEvent which is in a way similar to PublishSubject? Basic flow?
g
PublishSubject is not similar to SingleLiveEvent
PublishSubject analogue in Flow is ShardFlow with replay(0)
n
So SharedFlow with replay(0) would be go to solution for sending events from viewmodel to the view for things like snack bars and similar in flow land?
g
depends on case, but if you don’t want to lose event during configuration change, you need some way to cache it (so on attach it will be . But if it doesn’t matter, yes, it way to go It’s possible to achieve with broadcast channel and buffer, so first consumer will receive event, but I really not sure that it a good solution I prefer consumable event pattern for such cases
d
Why not SharedFlow with replay(1)?
Or StateFlow?
n
The idea behind single event live data was for the situations when you want to trigger navigation or similar only once, hooking state flow or basic live data would retrigger when user navigates back and will create infinite loop situation where you cannot go back.
👍🏼 1
o
@Nemanja Mladenović that's why it's better to use SharedFlow for navigation events and not StateFlow
n
But with replay(0) and not replay(1) as suggested by Dave. But this is just speculative from my part, I haven't tried this or anything
o
replay(0) gives your desired behaviour, not retriggering when user navigates back
👍 2
d
The problem mentioned by Andrey isn't covered though... what happens on configuration change?
n
if you use replay(0) nothing will happen if you use StateFlow or SharedFlow with replay(1) it will retrigger after orientation change
g
It will retrigger, but it even worse, because you got event, changed orientation, you got it again
I really think that consumable state event is the best solution, it allows to have simple StateFlow or SharedFlow with replay(1)
d
... consumable state ...
?
t
I'm sorry if I misunderstood something. I've tried SharedFlow, but it may not be a replacement for SingleLiveEvent. With replay = 1, it seems that when you observe, you just receive the value you are holding. This is the same as LiveData. If replay = 0 (default), the event cannot be received when there is no observer due to screen rotation etc. https://pl.kotl.in/IncXqXoGL
o
What is an usecase where you want to re-emit an event when you rotate your screen?
t
For example, the app rotates while communicating with the server, and the server returns an error, so if you want to display an error message in a snack bar or dialog, it will not be displayed.
However, it is a very rare case, so I may not have to worry too much about it. Also, if it is so important, it may be implemented to be held in StateFlow.
d
It's not so rare... I encountered it when my viewmodel got its initial state before the listener went up in the fragment's onCreate...
I ended up using replay = 1...
g
@dave08 sorry, I meant consumable event
👍🏼 1
But replay(1) will re-emit event even if it dispatched after configuration change
t
There seem to be several ways to do consumable events. Is there any recommended way? • Simply make the event a StateFlow and set Empty object after consuming it. • Wrap event with Event class to have consumed state. etc ..
g
I prefer second one, it doesn’t require exposing MutableStateFlow for consumer, so it’s much more flexible (so for example you can do filter or map of events)
👍 1
f
I send events via channels converted to Flows
after they're received they're gone even if I rotate the screen
o
@Florian you should consider using MutableSharedFlow over Channel's for events
f
yea I will look at it next, thank you
although there is a little caveat
you have to stop collecting in onStop, not onDestroyView
otherwise you can lose events beween onStop and onDestroy
so the view's lifecycleScope doesn't suffice
o
what is an usecase where you would want to receive events between onStop and onDestroyView?
f
I don't know, mabye a snackbar as the result of some long running operation
and while the app is in the background you could have a config change
o
Maybe I'm understanding it wrong, but if the fragment's onDestroyView() is called, you're probably navigated away from the screen. So showing a snackbar on screen A, when you navigated away from it, seems like an unusual thing.
f
In this case you don't need it, right. But onDestroyView is also called if you put your app into the background and have a configuration change
like rotating the device
this actually loses events
r
Channel + Flow might be a good replacement for SingleLiveEvent. Refer A use-case for channels in https://elizarov.medium.com/shared-flows-broadcast-channels-899b675e805c
f
thanks