Room : LiveData from Dao will trigger Observer.onChanged on every Update, even if the LiveData value has no change

There is simple solution in Transformations method distinctUntilChanged.expose new data only if data was changed.

In this case we get data only when it changes in source:

LiveData<YourType> getData(){
    return Transformations.distinctUntilChanged(LiveData<YourType> source));
}

But for Event cases is better to use this: https://stackoverflow.com/a/55212795/9381524


This situation is known as false positive notification of observer. Please check point number 7 mentioned in the link to avoid such issue.

Below example is written in kotlin but you can use its java version to get it work.

fun <T> LiveData<T>.getDistinct(): LiveData<T> {
    val distinctLiveData = MediatorLiveData<T>()
    distinctLiveData.addSource(this, object : Observer<T> {
        private var initialized = false
        private var lastObj: T? = null
        override fun onChanged(obj: T?) {
            if (!initialized) {
                initialized = true
                lastObj = obj
                distinctLiveData.postValue(lastObj)
            } else if ((obj == null && lastObj != null) 
                       || obj != lastObj) {
                lastObj = obj
                distinctLiveData.postValue(lastObj)
            }
        }
    })
    return distinctLiveData
}

I stuck with the same problem.

What i did wrong:

1) creating anonimous object:

private LiveData<List<WordsTableEntity>> listLiveData;
// listLiveData = ... //init our LiveData...
listLiveData.observe(this, new Observer<List<WordsTableEntity>>() {
        @Override
        public void onChanged(@Nullable List<WordsTableEntity> wordsTableEntities) {

        }
    });

In my case, I called the method several times in which this line was located.

From the docs i supposed, that new Observers take data from LiveData. Because of that, author could receive few onChanged methods from few new anonimous Observers, if he set observe userDao.getAllNamesOfUser().observe(this, new Observer that way.

Its will be better to create named Observer object before LiveData.observe(... and once

@Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        observer = new Observer<List<WordsTableEntity>>() {
            @Override
            public void onChanged(@Nullable List<WordsTableEntity> wordsTableEntities) {
                adapter.setWordsTableEntities(wordsTableEntities);
                progressBar.setVisibility(View.GONE);
            }
        };
    }

and then set it LiveData.observe(observer and we receive data from LieData first time and then, when data will be changed.

2) Observing one Observe object multiple times

public void callMethodMultipleTimes(String searchText) {
            listLiveData = App.getRepositoryRoomDB().searchDataExceptChapter(searchText);
            listLiveData.observe(this, observer);
    }

I calling this method multiple times and debug showed me, that i was adding my observer as many times, as i called callMethodMultipleTimes();

Our listLiveData is a global variable and it lives. It changes the object reference here

listLiveData = App.getRepositoryRoomDB().searchDataExceptChapter(searchText);

, but the old object in memory is not immediately deleted

This will be fixed, if we call listLiveData.removeObserver(observer); before

listLiveData = App.getRepositoryRoomDB().searchDataExceptChapter(searchText);

And returning to 1) - we can not call listLiveData.removeObserver(our anonimous Observer); because we do not have an anonymous object reference.

So, in the result we can do so:

private Observer observer;
private LiveData<List<WordsTableEntity>> listLiveData;
@Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        observer = new Observer<List<WordsTableEntity>>() {
            @Override
            public void onChanged(@Nullable List<WordsTableEntity> wordsTableEntities) {
                adapter.setWordsTableEntities(wordsTableEntities);
                progressBar.setVisibility(View.GONE);
            }
        };
    }

public void searchText(String searchText) {
            if (listLiveData != null){
                listLiveData.removeObservers(this);
            }
            listLiveData = App.getRepositoryRoomDB().searchDataExceptChapter(searchText);
            listLiveData.observe(this, observer);
    }

I didn't use distinct functions. In my case it works without distinct.

I hope my case will help someone.

P.S. Version of libraries

    // Room components
    implementation "android.arch.persistence.room:runtime:1.1.1"
    annotationProcessor "android.arch.persistence.room:compiler:1.1.1"
    androidTestImplementation "android.arch.persistence.room:testing:1.1.1"

    // Lifecycle components
    implementation "android.arch.lifecycle:extensions:1.1.1"
    annotationProcessor "android.arch.lifecycle:compiler:1.1.1"