preface
According to Jepack official documentation:
LiveData is an observable data store class. Unlike regular observable classes, LiveData has lifecycle awareness, meaning that it follows the lifecycle of other application components such as activities, fragments, or services. This awareness ensures that LiveData updates only application component observers that are in an active lifecycle state.
If an Observer (represented by an Observer class) is in a STARTED or RESUMED state during its lifecycle, LiveData considers the Observer to be active. LiveData only notifies active observers of updates. Inactive observers registered to observe LiveData objects do not receive notification of changes.
You can register observers that are paired with objects that implement the LifecycleOwner interface. With this relationship Lifecycle objects can be removed when the state of the corresponding Lifecycle object changes to DESTROYED. This is especially useful for activities and fragments because they can safely observe LiveData objects without fear of leakage (the system unsubscribe them immediately when the Activity and Fragment’s life cycle is destroyed).
We now know that LiveData enables data updates to be sensed by the Observer in observer mode, and this perception only occurs in the active lifecycle state of the LifecycleOwner.
So how does it sense life cycle changes? Do I need to cancel my registration? Will subscribed observers receive the same value when setting the same value? Also, what are sticky events, and how do you prevent data flooding? Take a look at the source code implementation with these questions in mind.
How does LiveData sense life cycle awareness
When using liveData. observe subscribers, this is called
edPacketVm.grabRedPacketLiveData.observe(this, Observer {
.....
})
Copy the code
Let’s start with the Livedata.observer method:
### LiveData
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// DESTROYED in the lifecycle, do not subscribe
return;
}
// Wrap the object with LifecycleBoundObserver
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
//LifecycleBoundObserver is a subclass or implementation of ObserverWrapper
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
You cannot add the same Observer to two different lifecycle objects.
if(existing ! =null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if(existing ! =null) {
return;
}
// Have the ability to perceive the life cycle
owner.getLifecycle().addObserver(wrapper);
}
Copy the code
LifecycleOwner is DESTROYED; return. Components in the DESTROYED state are not allowed to be registered.
The LifecycleBoundObserver class is then created, wrapped around the Observer, and the putIfAbsent method is called to store the owner and Obseerver one-key value pairs into the mObservers.
Finally, this will be added to Lifecycle via owner.getlifecycle ().addobServer so that when we call the Observer method, Lifecycle observer will actually be added inside LiveData. This gives LiveData the ability to observe the life cycle of components.
Observer event callback
Let’s look at the key class LifecycleBoundObserver. How does the callback work?
// The observer implementation
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
@NonNull
final LifecycleOwner mOwner;
boolean mActive;
int mLastVersion = START_VERSION;
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
super(observer);
mOwner = owner;
}
@Override
boolean shouldBeActive(a) {
// Determine whether the current incoming component is Active
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
// Get the life cycle
Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
if (currentState == DESTROYED) {
// If it is destroyed, call livedata.removeObserver to remove it
removeObserver(mObserver);
return;
}
Lifecycle.State prevState = null;
// Two state changes are also called back
while(prevState ! = currentState) { prevState = currentState;// See belowactiveStateChanged(shouldBeActive()); currentState = mOwner.getLifecycle().getCurrentState(); }}@Override
boolean isAttachedTo(LifecycleOwner owner) {
return mOwner == owner;
}
@Override
void detachObserver(a) {
mOwner.getLifecycle().removeObserver(this); }}Copy the code
LifecycleBoundObserver implements the LifecycleEventObserver method, which implements the lifecycle state callback in the onStateChanged method. Removes the observer when the state is in a DESTROYED state. This is why Livedata does not need to be unregistered. Therefore, an observer is not notified when they are in a DESTROYED state.
Take a look at the implementation of activeStateChanged:
private abstract class ObserverWrapper {
final Observer<? super T> mObserver;
boolean mActive;
intmLastVersion = START_VERSION; .void activeStateChanged(boolean newActive) {
// If mActive is not assigned, the default value is false
// When we just called activeStateChanged, the value passed in is true as returned by shouldBeActive()
if (newActive == mActive) {
// If the active state has not changed, it will not be processed.
return;
}
mActive = newActive;
// Extend Livedata based on Active state and the number of components in Active state
changeActiveCounter(mActive ? 1 : -1);
if (mActive) {
// When the observer becomes active, data is distributed
dispatchingValue(this); }}}// Implement it
void changeActiveCounter(int change) {
int previousActiveCount = mActiveCount;
mActiveCount += change;
if (mChangingActiveState) {
return;
}
mChangingActiveState = true;
try {
while(previousActiveCount ! = mActiveCount) {boolean needToCallActive = previousActiveCount == 0 && mActiveCount > 0;
boolean needToCallInactive = previousActiveCount > 0 && mActiveCount == 0;
previousActiveCount = mActiveCount;
if (needToCallActive) {
// The number of active observers changes from 0 to 1
onActive();
} else if (needToCallInactive) {
// The number of active observers changed from 1 to 0onInactive(); }}}finally {
mChangingActiveState = false; }}Copy the code
The activityStateChange method resides in ObserverWrapper and is an inner class to LiveData. Internally, onActive and onInactive, respectively, are called based on the number of Active components in A, which belong to the callback methods used by the Livedata extension.
When the observer is active, the dispatchingValue method is called for data distribution.
void dispatchingValue(@Nullable ObserverWrapper initiator) {
// If it is being distributed, the distribution is invalid
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
// The tag is being distributed
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if(initiator ! =null) {
considerNotify(initiator);
initiator = null;
} else {
//observerWrapper is empty, traversing notifies all observers
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break; }}}}while (mDispatchInvalidated);
The //// tag is not in the distribution state
mDispatchingValue = false;
}
Copy the code
This does not continue distribution if it is currently being distributed. When you step down, the notice method is called regardless of whether ObserverWrapper is null. Let’s look at the notice method.
public LiveData(a) {
mData = NOT_SET;
// Initial assignment
mVersion = START_VERSION;
}
private void considerNotify(ObserverWrapper observer) {
if(! observer.mActive) {return;
}
// Judge is active
// If the owner corresponding to the current Observer is inactive, the activeStateChanged method is called again and false is passed, which is evaluated internally again
if(! observer.shouldBeActive()) { observer.activeStateChanged(false);
return;
}
//mLastVersion does not have an initial value
//mVersion +1 each time the setValue method is called
/ / vesion of judgment
if (observer.mLastVersion >= mVersion) {
return;
}
//mLastVersion is only assigned here
observer.mLastVersion = mVersion;
// This finally calls the onChanged method of the Observer passed in by our livedata. observe method.
observer.mObserver.onChanged((T) mData);
}
Copy the code
If inactive, return, and activeStateChanged again, passing false, internally judged again. The onChange method is eventually called, which is the observer event callback.
Here we see that the version check is done, but not the same value, so the subscribed observers will still receive the same value when the same value is set.
Data update postValue/setValue
Now let’s look at how postValue and setValue update data.
/ / postValue calls
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
// The child thread can execute again
if(! postTask) {return;
}
// Finally call mainHandler. post(runnable) to do a thread switch
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
// Child thread implementation
private final Runnable mPostValueRunnable = new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run(a) {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
// Finally call setValuesetValue((T) newValue); }};Copy the code
The posetValue method puts mPostValueRunnable on the main thread, and in the run method main eventually calls the setValue method.
# # setValue callsprotected void setValue(T value) {
// Check whether the current thread is in the main thread
assertMainThread("setValue");
/ / version
mVersion++;
mData = value;
// Also distribute values
dispatchingValue(null);
}
Copy the code
You can see that setValue is running in the main thread, internally calling the dispatchingValue method. And this is what we know from the above analysis, the dispatchingValue method with a null parameter is going to go through and notify all the observers.
So both the setValue and postValue methods call the dispatchingValue method.
How to use observeForever
If there are observers that will not be removed, there is an answer.
public void observeForever(@NonNull Observer<? super T> observer) {
assertMainThread("observeForever");
// Wrap class AlwaysActiveObserver
AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing instanceof LiveData.LifecycleBoundObserver) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if(existing ! =null) {
return;
}
wrapper.activeStateChanged(true);
}
Copy the code
Within a method called directly the wrapper. ActiveStateChanged (true), was introduced into the true, to ensure the internal finally will go dispatchingValue method, with the different packing type AlwaysActiveObserver again at the same time, AlwaysActiveObserver code
private class AlwaysActiveObserver extends ObserverWrapper {
AlwaysActiveObserver(Observer<? super T> observer) {
super(observer);
}
@Override
boolean shouldBeActive(a) {
// It is very simple to make it independent of the life cycle and write the return value to true
return true; }}Copy the code
Quite simply, it does not depend on the life cycle to determine whether it is active or not, and simply writes the return value to true. ShouldBeActive () when considered as an observer, let the component remain Active.
How do sticky events happen
Having completed the analysis of LiveData source code, now we will analyze the problems encountered in the development:
Why does a subscriber observer called LiveData# Observe receive an old value when LiveData has a value?
When a new observer is registered, the observed sends the old value to the observer. Now let’s return to notice:
private void considerNotify(ObserverWrapper observer) {
if(! observer.mActive) {return; }...// since the observer is newly created, observer.mlastversion should be the initial value LiveData#START_VERSION -1
Since LiveData is not the first distribution value, mVersion must be greater than the initial value start_version-1
// This condition is invalid
if (observer.mLastVersion >= mVersion) {
return;
}
// Assign the mVersion of LiveData to observer.mlastVersion
observer.mLastVersion = mVersion;
// Call onChanged which is the onChanged of the Observer object passed in livedata. observe
observer.mObserver.onChanged((T) mData);
}
Copy the code
Obser.mlaseversion has not yet been assigned because the observer was created when the new observer subscribed, and is equal to START_VERSION (-1). This is not the first time that mVersion has been distributed, so mVersion must be greater than the initial value of start_version-1. Therefore, observer.mLastVersion >= mVersion caused the sticky event exception.
The specific solution is to look at LiveData data inversion: don’t ask, ask is unanticipated and refer to unpeek-LiveData source code.
To summarize, there are several cases where LiveData will distribute values:
- Call setValue and postValue and LifecycleOwner is active
- Call LiveData# Observe subscriber observer when LiveData has a value and is active
- LiveData has a new value, that is, mLastVersion of ObserverWrapper is less than mVersion of LiveData, LifecycleOwner goes from inactive to active
conclusion
Finally, the relational class diagram of LiveData:
Pictures from — a guide to Understand LiveData(Principles)
In the process of analyzing the source code, tracking most of the associated classes are in it, combined with this picture is not to have a clearer understanding of LiveData.
reference
LiveData Official Documentation – Overview of LiveData
Jetpack AAC complete parsing (ii) Full mastery of LiveData
This article introduces you to LiveData(Principles)
Learn Android again: LiveData data backflow background reason full picture exclusive analysis