Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I found that the LiveData returned by Dao will call its observer whenever the row is updated in DB, even if the LiveData value is obviously not changed.

Consider a situation like the following example :

Example entity

@Entity public class User {     public long id;     public String name;     // example for other variables     public Date lastActiveDateTime; } 

Example Dao

@Dao public interface UserDao {     // I am only interested in the user name     @Query("SELECT name From User")     LiveData<List<String>> getAllNamesOfUser();      @Update(onConflict = OnConflictStrategy.REPLACE)     void updateUser(User user); } 

Somewhere in background thread

UserDao userDao = //.... getting the dao User user = // obtain from dao.... user.lastActiveDateTime = new Date(); // no change to user.name userDao.updateUser(user); 

Somewhere in UI

// omitted ViewModel for simplicity userDao.getAllNamesOfUser().observe(this, new Observer<List<String>> {     @Override     public void onChanged(@Nullable List<String> userNames) {         // this will be called whenever the background thread called updateUser.          // If user.name is not changed, it will be called with userNames          // with the same value again and again when lastActiveDateTime changed.     } }); 

In this example, the ui is only interested to user name so the query for LiveData only includes the name field. However the observer.onChanged will still be called on Dao Update even only other fields are updated. (In fact, if I do not make any change to User entity and call UserDao.updateUser, the observer.onChanged will still be called)

Is this the designed behaviour of Dao LiveData in Room? Is there any chance I can work around this, so that the observer will only be called when the selected field is updated?


Edit : I changed to use the following query to update the lastActiveDateTime value as KuLdip PaTel in comment suggest. The observer of LiveData of user name is still called.

@Query("UPDATE User set lastActiveDateTime = :lastActiveDateTime where id = :id") void updateLastActiveDateTime(Date lastActiveDateTime, int id); 
like image 983
reTs Avatar asked Nov 10 '17 04:11

reTs


People also ask

In which method should you observe a LiveData object?

Attach the Observer object to the LiveData object using the observe() method. The observe() method takes a LifecycleOwner object. This subscribes the Observer object to the LiveData object so that it is notified of changes. You usually attach the Observer object in a UI controller, such as an activity or fragment.

How LiveData is lifecycle aware?

LiveData is a lifecycle-aware component and thus it performs its functions according to the lifecycle state of other application components. Further, if the observer's lifecycle state is active i.e., either STARTED or RESUMED, only then LiveData updates the app component.

How to update LiveData android?

As it stands, LiveData does not have any publicly available methods to update the stored data, because of this, we should instead use MutableLiveData. This class is essentially the same as LiveData, with the simple difference being that it provides us with the setValue(T) and postValue(T) methods.

What is Mutable Live data in android?

MutableLiveData. MutableLiveData is just a class that extends the LiveData type class. MutableLiveData is commonly used since it provides the postValue() , setValue() methods publicly, something that LiveData class doesn't provide.


2 Answers

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

like image 73
Jurij Pitulja Avatar answered Sep 22 '22 03:09

Jurij Pitulja


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 } 
like image 36
Pinakin Kansara Avatar answered Sep 22 '22 03:09

Pinakin Kansara