Search code examples
viewmodelobserver-patternandroid-livedatamutablelivedata

MutableLiveData does not notify observer


I am trying to use MutableLiveData with pre-filled value from database, but Observer always returns null as book. I need to keep it as MutableLiveData, NOT LiveData, because I have to set it programatically on some places. Therefore I am calling setValue with LiveData retrieved from DB. Code:

ViewModel:

private final MutableLiveData<Book> bookLiveData;

public MyViewModel(@NonNull Application application) {
    super(application);
    ...
    book = new MutableLiveData<>();
    bookLiveData.setValue(bookRep.getBookLiveData().getValue());
    ....
}

Fragment:

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    ...
    getActivity().getBookLiveData().observe(getViewLifecycleOwner(), book -> {
            ... // When fragment is created, book is always null here
    });
}

Can somebody tell me why is Book null in observer? I thought that Observer would be notified when bookRep.getBookLiveData() is finished and value is retrieved. THank you for your help.


Solution

  • bookRep.getBookLiveData().getValue() will always return null. Since you are setting a null value in the ViewModel like this: bookLiveData.setValue(null) at line 7, bookLiveData is always null at initial launch.

    You cannot use getValue() on a LiveData object to get value here because it is always null at initialization. You need to observe so that you get notified when it actually emits a value.

    There are several different ways to solve it. One way is to observe getBookLiveData() in the ViewModel using observeForever() method.

    private LiveData<Book> bookFromRep;
    private MutableLiveData<Book> bookLiveData = new MutableLiveData<>();
    private Observer<Book> bookObserver = bookLiveData::postValue; // Book observer posts value to bookLiveData.
    
    public MyViewModel(@NonNull Application application) {
        super(application);
        ...
        bookFromRep = bookRep.getBookLiveData();
        bookFromRep.observeForever(bookObserver);
        ....
    }
    
    @Override
    protected void onCleared() {
        super.onCleared();
    
        // Don't forget to remove observer when you use observeForever()
        bookFromRep.removeObserver(bookObserver);
    }
    

    Another, and arguably a better, approach to this problem is to use MediatorLiveData. I won't include its implementation here but what I recommend is to understand different approaches and find the best approach that fits your need.