My understanding on LiveData is that, it will trigger observer on the current state change of data, and not a series of history state change of data.>
I snatched Vasiliy's fork of your fork of the fork and did some actual debugging to see what happens.
Might be related to the way ComputableLiveData offloads onActive() computation to Executor.
Close. The way Room's LiveData expose works is that it creates a >
ComputableLiveData, which keeps track of whether your data set has been invalidated underneath in Room.
trashedNotesLiveData = NoteplusRoomDatabase.instance().noteDao().getTrashedNotes();
So when the note table is written to, then the InvalidationTracker bound to the LiveData will call invalidate() when a write happens.
@Override
public LiveData> getNotes() {
final String _sql = "SELECT * FROM note where trashed = 0";
final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
return new ComputableLiveData>() {
private Observer _observer;
@Override
protected List compute() {
if (_observer == null) {
_observer = new Observer("note") {
@Override
public void onInvalidated(@NonNull Set tables) {
invalidate();
}
};
__db.getInvalidationTracker().addWeakObserver(_observer);
}
Now what we need to know is that ComputableLiveData's invalidate() will actually refresh the data set, if the LiveData is active.
// invalidation check always happens on the main thread
@VisibleForTesting
final Runnable mInvalidationRunnable = new Runnable() {
@MainThread
@Override
public void run() {
boolean isActive = mLiveData.hasActiveObservers();
if (mInvalid.compareAndSet(false, true)) {
if (isActive) { // <-- this check here is what's causing you headaches
mExecutor.execute(mRefreshRunnable);
}
}
}
};
Where liveData.hasActiveObservers() is:
public boolean hasActiveObservers() {
return mActiveCount > 0;
}
So refreshRunnable actually runs only if there is an active observer (afaik means lifecycle is at least started, and observes the live data).
This means that when you subscribe in TrashFragment, then what happens is that your LiveData is stored in Activity so it is kept alive even when TrashFragment is gone, and retains previous value.
However, when you open TrashFragment, then TrashFragment subscribes, LiveData becomes active, ComputableLiveData checks for invalidation (which is true as it was never re-computed because the live data was not active), computes it asynchronously on background thread, and when it is complete, the value is posted.
So you get two callbacks because:
1.) first "onChanged" call is the previously retained value of the LiveData kept alive in the Activity's ViewModel
2.) second "onChanged" call is the newly evaluated result set from your database, where the computation was triggered by that the live data from Room became active.
So technically this is by design. If you want to ensure you only get the "newest and greatest" value, then you should use a fragment-scoped ViewModel.
You might also want to start observing in onCreateView(), and use viewLifecycle for the lifecycle of your LiveData (this is a new addition so that you don't need to remove observers in onDestroyView().
If it is important that the Fragment sees the latest value even when the Fragment is NOT active and NOT observing it, then as the ViewModel is Activity-scoped, you might want to register an observer in the Activity as well to ensure that there is an active observer on your LiveData.