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