This is the second day of my participation in the August More text Challenge. For details, see: August More Text Challenge
An overview of the
The official doc
LiveData belongs to the architecture component of Jetpack, which is designed to notify the upper UI when changes are made to the underlying data. It is very comfortable to use with the MVVM architecture.
Personal understanding of it is divided into two parts:
LivaData=Live+Data, Live means it is alive, or it can sense changes in the life cycle. Data refers to its nature or a Data storage-related class.
2. The actual implementation is the observer model. The LiveData observer (such as the anonymous observer we passed in with the observe method) is bound to the Lifecycle object (usually the Activity/Fragment). Updates to LiveData (observed) will be notified to the observer (callback to the onChanged method) when it is active (e.g. when the corresponding Activity is started, resumed).
The advantages of LiveData are as follows: (1) Most importantly, as the observed, LiveData can sense the life cycle of the observer (Activity/Fragment), and manage its own life cycle. As mentioned above, LiveData updates can only be received when the Activity or Fragment is active. This ensures that the UI status can be updated when the data is updated. ③ According to the change of the life cycle of the observer, self-cleaning can be carried out after the destruction of the observer to avoid memory leakage. (4) When the interface is restored to the active state or re-created, the latest data is immediately received.
usage
See official documentation.
Analysis:
LiveData itself is an abstract class, and the only subclass in the Jetpack component is MutableLiveData. Two methods, setValue and postValue, are exposed. Two methods are used to update the value of LiveData, the former can only be called in the main thread, the latter can be called in the child thread, through the Handler message mechanism, notify the main thread update.
Before we analyze these two methods, let’s look at how an observer subscribs to LiveData, LiveData’s observe method:
// The first argument is the holder of Lifecycle, such as an Activity or Fragment
// The second argument is the observer object. We usually create an anonymous object directly
public void observe(LifecycleOwner owner, Observer<T> observer) {
// If the observer is already destoryed when subscribed to LiveData, skip the subscription directly
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
return;
}
// Wrap the owner and observer as LifecycleBoundObserver objects. This object mainly encapsulates
// Lifecycle will be assigned to a value at any time and will unsubscribe observers when the status changes.
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
// mObservers is a linked list structure (containing Entry
) that supports changes during iteration.
,v>
// The list stores the observer and the wrapper object above. Note that the list already stores an entry with an observer key
// Instead of updating the entry, return the value directly.
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
// If the value returned is not null, then determine the owner of the observer and the owner of the new passed in
If yes, it means that the observer is already bound to a certain owner, and cannot be bound repeatedly
if(existing ! =null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException( "Cannot add the same observer"
+ " with different lifecycles" );
}
// If the value returned is not null, but does not enter the above exception throw
// This time the owner is the same. There is no need to bind the same owner twice
if(existing ! =null) {
return;
}
// Add a lifecycle observer to the owner so that the wrapper, or observer, is aware of it
// When the owner(Activity/Fragment) lifecycle changes,
// You can call onStateChanged to the wrapper to update the active state
owner.getLifecycle().addObserver(wrapper);
}
Copy the code
LifecycleBoundObserver onStateChanged method:
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
// If the lifetime of the component is in the destroyed state, then the removeObserver is automatically removed.
// This removes the reference to the LifecyclerOwner from the observer to avoid memory leaks
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
// If the Owner is not Destoryed, then the Owner will update to the active state of LiveData when the life cycle of the Owner changes
activeStateChanged(shouldBeActive());
}
Copy the code
Above is the analysis related to the life status of LiveData, and then look at the assignment operation to LiveData.
setValue:
protected void setValue(T value) {
assertMainThread( "setValue" );// IllegalStateException is not the main thread
mVersion++;// Mark the current data version
mData = value;/ / assignment data
dispatchingValue(null);
}
Copy the code
The setValue call dispatchingValue is passed null:
Private void dispatchingValue(@nullable ObserverWrapper Initiator) { Return if (mDispatchingValue) {mDispatchInvalidated = true; return; } mDispatchingValue = true; do { mDispatchInvalidated = false; if (initiator ! = null) {// Follow the notice (initiator); initiator = null; } else {// Traverse the list of mObservers, To decide whether to need to notify observers for updates (Iterator < Map. The Entry < Observer < T >, ObserverWrapper > > Iterator. = mObservers iteratorWithAdditions (); iterator.hasNext(); ) { considerNotify(iterator.next().getValue()); if (mDispatchInvalidated) { break; } } } } while (mDispatchInvalidated); mDispatchingValue = false; }Copy the code
considerNotify:
private void considerNotify(ObserverWrapper observer) {
// If the observer is not active, skip it
if(! observer.mActive) {return;
}
// There may be a change in the life cycle of the Owner, but LiveData has not received the notification.
// Obtain the Owner's current (latest) life status
// shouldBeActivie returns true in both cases
// STARTED: refers to the Activity lifecycle call after onStart, before onPause
// RESUMED: after onResume is called
if(! observer.shouldBeActive()) {// If the life state of the Owner is not active, update here
observer.activeStateChanged(false);
return;
}
// If the latest version of the data is not greater than the last version of the data changed, then return
if (observer.mLastVersion >= mVersion) {
return;
}
// Update the last changed version value
observer.mLastVersion = mVersion;
// The new data is passed in to the onChanged method of the observer
// The onChanged method is overwritten by the anonymous object passed in when we subscribe to LiveData
// At this point, the observer is notified of changes in The LiveData data.
observer.mObserver.onChanged((T) mData);
}
Copy the code
postValue:
PostValue is used in the subthread to notify the main thread that liveData has changed, as follows:
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
// mPendingData
mPendingData = value;
}
if(! postTask) {return;
}
// Throw the mPostValueRunnable task to the main thread
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
Copy the code
Let’s start with mPostValueRunnable:
private final Runnable mPostValueRunnable = new Runnable() {
@Override
public void run() {
// Complete the data update in the main thread
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
// Then restore mPendingData to the unset state
// mPendingData is used to help ensure that data updates are made in the main thread
mPendingData = NOT_SET;
}
// postValue finally calls setValuesetValue((T) newValue); }};Copy the code
There is also a bit of ArchTaskExecutor, which is a separate part of the Jetpack architecture. Take a look at the next point:
TaskExecutor is an abstract base class that defines the ability to process Runnable in the child thread pool and the main thread, respectively.
DefaultTaskExecutor is the default implementation of TaskExecutor:
public class DefaultTaskExecutor extends TaskExecutor {
private final Object mLock = new Object();
// The IO thread pool is a fixed number of two thread pools
private ExecutorService mDiskIO = Executors.newFixedThreadPool(2);
@Nullable
private volatile Handler mMainHandler;
@Override
public void executeOnDiskIO(Runnable runnable) {
mDiskIO.execute(runnable);
}
@Override
public void postToMainThread(Runnable runnable) {
// Runnable Post back to the main thread through the Handler associated with the main thread
if (mMainHandler == null) {
synchronized (mLock) {
if (mMainHandler == null) {
mMainHandler = new Handler(Looper.getMainLooper());
}
}
}
mMainHandler.post(runnable);
}
@Override
public boolean isMainThread() {
returnLooper.getMainLooper().getThread() == Thread.currentThread(); }}Copy the code
ArchTaskExecutor is also an implementation of TaskExecutor, and unlike DefaultTaskExecutor, An ArchTaskExecutor handles all tasks through its mDelegate, a TaskExecutor proxy object. Through the ArchTaskExecutor’s set method, clients can inherit tasks from taskExecutors. Implement your own Runnable task processing logic.
@NonNull
private TaskExecutor mDelegate;The default implementation is mDefaultTaskExecutor
@NonNull
private TaskExecutor mDefaultTaskExecutor;/ / DefaultTaskExecutor instance
Copy the code