Why LiveData observer is being triggered twice for a newly attached observer

I forked your project and tested it a bit. From all I can tell you discovered a serious bug.

To make the reproduction and the investigation easier, I edited your project a bit. You can find updated project here: https://github.com/techyourchance/live-data-problem . I also opened a pull request back to your repo.

To make sure that this doesn't go unnoticed, I also opened an issue in Google's issue tracker:

Steps to reproduce:

  1. Ensure that REPRODUCE_BUG is set to true in MainFragment
  2. Install the app
  3. Click on "add trashed note" button
  4. Switch to TrashFragment
  5. Note that there was just one notification form LiveData with correct value
  6. Switch to MainFragment
  7. Click on "add trashed note" button
  8. Switch to TrashFragment
  9. Note that there were two notifications from LiveData, the first one with incorrect value

Note that if you set REPRODUCE_BUG to false then the bug doesn't reproduce. It demonstrates that subscription to LiveData in MainFragment changed the behavior in TrashFragment.

Expected result: Just one notification with correct value in any case. No change in behavior due to previous subscriptions.

More info: I looked at the sources a bit, and it looks like notifications being triggered due to both LiveData activation and new Observer subscription. Might be related to the way ComputableLiveData offloads onActive() computation to Executor.


I have introduced just one change in your code:

noteViewModel = ViewModelProviders.of(this).get(NoteViewModel.class);

instead of:

noteViewModel = ViewModelProviders.of(getActivity()).get(NoteViewModel.class);

in Fragment's onCreate(Bundle) methods. And now it works seamlessly.

In your version you obtained a reference of NoteViewModel common to both Fragments (from Activity). ViewModel had Observer registered in previous Fragment, I think. Therefore LiveData kept reference to both Observer's (in MainFragment and TrashFragment) and called both values.

So I guess the conclusion might be, that you should obtain ViewModel from ViewModelProviders from:

  • Fragment in Fragment
  • Activity in Activity

Btw.

noteViewModel.getTrashedNotesLiveData().removeObservers(this);

is not necessary in Fragments, however I would advise putting it in onStop.


The reason is that in your .observe() method, you passed a fragment as the lifecycle owner. What should have been passed is the viewLifecycleOwner object of the fragment

viewModel.livedata.observe(viewLifecycleOwner, Observer {
        // Do your routine here
    })