livedata observe event triggered at each fragment onStart

1.1k views Asked by At

I am having issues with LiveData.

I have 3 fragments A (menuFragment), B (doThingsFragment), C (displayMessageFragment). We can move from A to B, from B to C and finally from C to A.

On the onStart method of doThingsFragment, I observe LiveData From doThingsViewModel and I move to displayMessageFragment when this value changes.

doThingsViewModel :

private var _message : MutableLiveData<String> = MutableLiveData()
var message : LiveData<String> = _message 

fun someFunction() {
    _message.value = "blablabla"
}

doThingsFragment :

override fun onStart() {
    super.onStart()

    mViewModel!!.message.observe(
        viewLifecycleOwner,
        Observer<String>
        {
            msg ->
            if(!msg.isNullOrEmpty() && mViewModel!!.toastType.value != null){
                val args = Bundle()
                args.putString("message", msg)

                Navigation.findNavController(requireView()).navigate(R.id.mountUnmountValidationFragment, args)
            }
        })
}

It works great the first time I update _message (we go from B to C as expected). However, when I try to move from A to B again, we go into doThingsFragment observer callback again and I move on to fragment C before fragment B is even displayed.

Since _message value has not changed, I expect not to trigger the observer callback everytime doThingFragment.onStart method is called...

What am I missing ?

Thanks a lot !

2

There are 2 answers

0
cactustictacs On

LiveData works on an Observer pattern, where something registers as an observer, and every time the thing it's observing changes, it gets notified. It's worth reading the docs on this:

Adds the given observer to the observers list within the lifespan of the given owner. The events are dispatched on the main thread. If LiveData already has data set, it will be delivered to the observer.

When data changes while the owner [LifecycleOwner, e.g. the Activity] is not active, it will not receive any updates. If it becomes active again, it will receive the last available data automatically.

These are the important points - when you register to observe the data, you'll immediately be notified of the most recent value (if there is one), not just when there's a change. So you can't rely on that callback triggering as a signal that you need to move from B to C.

What you actually need to do depends on your app logic, how you can tell the difference between "fragment B just displayed, here's a value, and now I need to show fragment C" and "fragment B just displayed, here's a value, but I happen to know I don't need to show fragment C this time". You could do things with setting an initialised flag the first time you receive some data (might need to persist that state in onSaveInstanceState) or holding the last-seen value (harder to persist) but you get the idea

0
i30mb1 On

It is the way how livedata behave, if you need receiver event only one time, google or stackoverflow about SingleLiveData