https://kotlinlang.org logo
Title
f

Florian

11/10/2020, 6:04 PM
Are there any benefits LiveData still has over Flow? Or can Flow do anything LiveData can do?
👍 1
c

Casey Brooks

11/10/2020, 6:10 PM
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

Ian Lake

11/10/2020, 6:15 PM
With
StateFlow
and
SharedFlow
, there's no reason to purposefully choose LiveData for any new code you're writing
😍 2
☝️ 3
💪 3
😲 2
:kotlin-intensifies: 16
f

Florian

11/10/2020, 6:27 PM
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

zhuinden

11/10/2020, 6:30 PM
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

rkeazor

11/10/2020, 6:39 PM
LiveData is built to be lifecycle aware data holder
i

Ian Lake

11/10/2020, 6:43 PM
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

Florian

11/10/2020, 6:52 PM
that's interesting, thank you
is it planned to add
SavedStateHandke#getStateFlow
?
i

Ian Lake

11/10/2020, 6:57 PM
SavedStateHandle
 doesn't depend on coroutines (so it can't provide a 
StateFlow
 directly)
j

Jan Skrasek

11/10/2020, 7:04 PM
1) databinding. support for state flow is not there yet. 2) launch when started just suspend, doesn't stop collection of the flow.
f

Florian

11/10/2020, 7:21 PM
thank you everyone
z

zhuinden

11/10/2020, 8:31 PM
Oh nice, thanks! ☺️
f

Florian

11/10/2020, 9:16 PM
one downside of Flow that's left is that the syntax is quite verbose as compared to observing a LiveData
🤔 1
i

Ian Lake

11/10/2020, 11:36 PM
You can certainly write your own extension such as
/**
 * 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
viewModel.yourFlow.onEach { items ->
  // Do something with the items
}.launchWhenStartedIn(viewLifecycleOwner)
:kotlin-intensifies: 1
🙏 3
❤️ 6
d

dave08

11/11/2020, 3:17 AM
Funny that isn't provided in Android ktx libs...
t

takahirom

11/11/2020, 4:49 AM
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

Ian Lake

11/11/2020, 5:22 AM
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

gildor

11/11/2020, 3:07 PM
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

Nemanja Mladenović

11/12/2020, 7:11 AM
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

gildor

11/12/2020, 7:12 AM
PublishSubject is not similar to SingleLiveEvent
PublishSubject analogue in Flow is ShardFlow with replay(0)
n

Nemanja Mladenović

11/12/2020, 7:15 AM
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

gildor

11/12/2020, 7:20 AM
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

dave08

11/12/2020, 10:10 AM
Why not SharedFlow with replay(1)?
Or StateFlow?
n

Nemanja Mladenović

11/12/2020, 10:12 AM
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

Orhan Tozan

11/12/2020, 10:13 AM
@Nemanja Mladenović that's why it's better to use SharedFlow for navigation events and not StateFlow
n

Nemanja Mladenović

11/12/2020, 10:14 AM
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

Orhan Tozan

11/12/2020, 10:16 AM
replay(0) gives your desired behaviour, not retriggering when user navigates back
👍 2
d

dave08

11/12/2020, 10:24 AM
The problem mentioned by Andrey isn't covered though... what happens on configuration change?
n

Nemanja Mladenović

11/12/2020, 12:03 PM
if you use replay(0) nothing will happen if you use StateFlow or SharedFlow with replay(1) it will retrigger after orientation change
g

gildor

11/12/2020, 12:40 PM
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

dave08

11/12/2020, 12:48 PM
... consumable state ...
?
t

takahirom

11/12/2020, 2:35 PM
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

Orhan Tozan

11/12/2020, 3:20 PM
What is an usecase where you want to re-emit an event when you rotate your screen?
t

takahirom

11/12/2020, 3:26 PM
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

dave08

11/12/2020, 4:23 PM
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

gildor

11/12/2020, 4:35 PM
@dave08 sorry, I meant consumable event
👍🏼 1
But replay(1) will re-emit event even if it dispatched after configuration change
t

takahirom

11/13/2020, 1:35 AM
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

gildor

11/13/2020, 1:55 AM
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

Florian

11/13/2020, 3:14 PM
I send events via channels converted to Flows
after they're received they're gone even if I rotate the screen
o

Orhan Tozan

11/13/2020, 3:15 PM
@Florian you should consider using MutableSharedFlow over Channel's for events
f

Florian

11/13/2020, 3:15 PM
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

Orhan Tozan

11/13/2020, 3:17 PM
what is an usecase where you would want to receive events between onStop and onDestroyView?
f

Florian

11/13/2020, 3:19 PM
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

Orhan Tozan

11/13/2020, 3:23 PM
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

Florian

11/13/2020, 3:30 PM
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

Roshan P Varghese

11/29/2020, 4:54 PM
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

Florian

11/30/2020, 6:59 AM
thanks