LiveData and single events
LiveData
is great. You just set a value and the View will read that value whenever is needed. If the View is destroyed and then created again (e.g. when changing landscape <-> portrait configurations) the last value will be read.
So that's all, right? Every time we need to send something to the View we use a LiveData<T>
and we are done. Well, not exactly.
Since the latest value is read every time the View is created, what happens with things we want to send to the View exactly once? These could be messages shown in a toast, in a dialog, or navigation events. Using just LiveData
for these cases would mean that the message/dialog/navigation will happen every time the View is recreated, which is not what we want.
The tricky part is that this might not be obvious because the message/dialog/navigation will work at first. But as soon as you rotate your device and see the message/dialog to re-appear, or you navigate to a screen, go back only to be navigated forward again, you will know that something goes wrong.
So how to pass to the View these single-only events?
Use a wrapper
Maybe the best approach is to wrap your actual data needed for the event (e.g. the String to show in the dialog) in a wrapper that will return the inner value only once. This is to ensure that the value is only consumed once, even if the view is recreated (e.g. on orientation change).
This approach is the official recommended one, but it's quite verbose.
Use SingleLiveEvent
Another approach is to use the SingleLiveEvent
class. This is a class that was used in a Google example project and became a popular way to handle this issue, even though it was never truly recommended by Google.
The main reason is that if more than one observers are registered, only one will be called (without any way to know which one). If you can live with this, this an alternative, less verbose way, to send events from your ViewModel
.
Note that you would need to copy-paste this class into your project. It's not provided in any common library.
Happy sending values from your ViewModel
to your View! :)