15

I'm a bit confused on why the following code doesn't work:

MutableLiveData<String> mutableTest = new MutableLiveData<>();
MediatorLiveData<String> mediatorTest = new MediatorLiveData<>();
mediatorTest.addSource(mutableTest, test -> {
    Timber.d(test);
});
mutableTest.setValue("bla!");

This code seems straightforward, however the debugger doesn't enter the callback and nothing is logged to the console...

Edit: shouldn't this work then?

    MutableLiveData<String> mutableTest = new MutableLiveData<>();
    MediatorLiveData<String> mediatorTest = new MediatorLiveData<>();
    mediatorTest.observe(loginActivity, str -> Timber.d(str));
    mediatorTest.addSource(mutableTest, str -> Timber.d(str));
    mutableTest.setValue("bla!");
html_programmer
  • 18,126
  • 18
  • 85
  • 158
  • 5
    Nothing is observing `mediatorTest`. A lot of stuff gets skipped when there are no active observers. – CommonsWare Aug 14 '17 at 17:41
  • @CommonsWare Isn't `mutableTest` the observable and `mediatorTest` the observer in this example? Why would I need to observe `mediatorTest`? I couldn't find a simplified demo that gives me the result I expect. I thought `addSource` was supposed to add the listener to the `mediatorTest` observer, where `mutableTest` is passed as observable in the first param , and the lambda passed as callback in the second. – html_programmer Aug 14 '17 at 19:25
  • 3
    `mediatorTest` is both an observer and an observable. It's a `LiveData`, after all, which means it is an observable. "Why would I need to observe mediatorTest?" -- something has to consume the data. That's an observer. You have two `LiveData` objects, but only one is being observed. The primary use for `MediatorLiveData`, AFAIK, is for transformations, and there the transformation method (e.g., `map()`) returns the `MediatorLiveData`, which then gets observed to see the results of the transformation. – CommonsWare Aug 14 '17 at 19:42
  • 2
    FWIW, see [this sample app](https://github.com/commonsguy/cw-androidarch/tree/v0.2/General/LiveFilter) for a custom transformation (an RxJava-style `filter()`) and its application. I use a `MediatorLiveData` to observe a supplied `LiveData`, and then downstream consumers observe the `MediatorLiveData`. Also, see the source to the `Transformations` class from the library. – CommonsWare Aug 14 '17 at 19:43
  • @CommonsWare I've tried adding an observer, but the callback still doesn't get called for me... Is there something fundamentally wrong with this syntax? I just try to make this basic example work without the use of Transformations. – html_programmer Aug 14 '17 at 20:31
  • @CommonsWare Scratch that, the callback gets called, only the debugger didn't step into that. Thanks! If you write an answer, I will accept it. – html_programmer Aug 14 '17 at 20:43
  • 2
    Since I don't have an example of using a `MediatorLiveData` outside of a transformation method, I suggest that you answer your own question, showing what you wound up with (particularly if that differs from your edit). – CommonsWare Aug 14 '17 at 20:53
  • @CommonsWare Could the MediatorLiveData object be made into a publish/subscribe kind of object? Where the ViewModel has a MutableLiveData> field and whenever I add a new task to the list from my Activity/Fragment a MediatorLiveData object that observes the MutableLiveData object would persist this new Task to a RoomDatabase? – Bohsen Sep 25 '17 at 11:09
  • @Bohsen: Sorry, but I do not understand your proposed architecture. You might want to ask a separate Stack Overflow question. – CommonsWare Sep 25 '17 at 13:22
  • @CommonsWare Thanks for answering a comment on an old question. Will see if I can find the time to do some exploring. – Bohsen Sep 25 '17 at 22:28

2 Answers2

33

This answer is largely reproduction of what @CommonsWare has already shared in the comment section above.

In order for the callback on MediatorLiveData's addSource method to be triggered, the MediatorLiveData object needs to be observed itself as well.

The logic behind this is that the 'mediator' mediates between a LiveData object that it observes, and a final consumer of the data. The mediator is hence an observer and observable simultaneously, and the callback on addSource won't be triggered for the mediator when there are no active observers.

As an example; according to Google's Android Architecture Components, an activity or fragment could have an observer observing a mediator on the ViewModel, which in turn may observe other LiveData objects that are handled within the ViewModel or a referenced to an utility class.

@CommonsWare pointed out the use of the Transformation class that exposes methods map and switchMap, but these were not within scope of my use case although they are worth checking out.

html_programmer
  • 18,126
  • 18
  • 85
  • 158
2

I got here since I had more or less the same experience, but instead with MediatorLiveData.getValue(). I wasn't aware that was a problem until I faced it big time. My problem can be stated like this:

MutableLiveData<String> mutableTest = new MutableLiveData<>();
MediatorLiveData<String> mediatorTest = new MediatorLiveData<>();
mediatorTest.addSource(mutableTest, test -> {
    mediatorTest.value = test;
});
mutableTest.setValue("bla!");
mediatorTest.getValue(); // will be null

I know it's a bit simplified, but nevertheless MediatorLiveData.getValue() will not contain "bla" and in that way you never really know if you can trust getValue() unless you're 100% sure it is active (has at least one observer).

Same issue is the case for Transformations.map(...) and Transformations.switchMap(...), where getValue() of the returned LiveData doesn't necessarily returns the newest value unless it's observed.

Max_Payne
  • 142
  • 1
  • 1
  • 12
Anigif
  • 872
  • 8
  • 17
  • 1
    :"(has more than one observer)", Actually it should have more that zero observer. In your example your MediatorLiveData is not observed so it's value will not changed until it observed by some observer like activity or fragment. – David Sep 02 '20 at 05:56
  • 1
    @David Yeah, was a typo I didn't realize - has been fixed now. Thanks! – Anigif Apr 19 '23 at 09:53