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"