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);
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.
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.
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.
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.
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 }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With