preface

Hello, I’m Xiao Yi! The students who use Jetpack family bucket believe that they are familiar with LiveData, which can avoid memory leakage to a large extent, better separation of View layer and Model layer and many other advantages make LiveData become an indispensable tool in MVVM mode development. Today we’ll take a look at LiveData and see why it smells so good!

recommended

The article will be first published in the public account “Code Full” and personal blog “Li Yi’s small station”, please follow!

1. Common methods of LiveData

The implementation of LiveData is mainly based on the Observer mechanism. LiveData is the observed, and its Observer is the Observer. When the data of LiveData changes, it will notify its observers to update the data. The LiveData methods are as follows:

  • setVaule
  • postValue
  • observe
  • observeForever

Second, the setValue

Both setVaule and postVaule are used to modify the value of LiveData.

@MainThread
protected void setValue(T value) {
	assertMainThread("setValue");
	mVersion++;
	mData = value;
	dispatchingValue(null);
}
Copy the code

In the above code, there is an annotation @mainthread indicating that setValue can only be called from the MainThread, and an assertMainThread(“setValue”) at the beginning of the method to determine if the current call is in the MainThread, and to throw an exception if it is not.

MVersion ++ is used to record whether there is an update of LiveData. Each time there is an update, mVersion will be increased by one, which will be used later.

MData = value is easy to understand, it’s simply a record of the value that needs to be updated.

DispatchingValue (null) is used to notify observers to update data.

void dispatchingValue(@Nullable ObserverWrapper initiator) {
	if (mDispatchingValue) {
		mDispatchInvalidated = true;
		return;
	}
	mDispatchingValue = true;
	do {
		mDispatchInvalidated = false;
		if(initiator ! =null) {
			considerNotify(initiator);
			initiator = null;
		} else {
			for (Iterator<Map.Entry<Observer<? super T>, 
			ObserverWrapper>> iterator = mObservers.iteratorWithAdditions();
			iterator.hasNext();
			) {
				considerNotify(iterator.next().getValue());
				if (mDispatchInvalidated) {
					break; }}}}while (mDispatchInvalidated);
		mDispatchingValue = false;
	}
Copy the code

The parameter of dispatchingValue(@Nullable ObserverWrapper Initiator) is the observer. If an observer is passed in, only this incoming observer is notified of the update. Otherwise, all observers under the LiveData will be notified of the update one by one. In the code above, the notice () method actually notifies the observer to update the data. The source code looks like this:

private void considerNotify(ObserverWrapper observer) {
	if(! observer.mActive) {return;
	}
	if(! observer.shouldBeActive()) { observer.activeStateChanged(false);
		return;
	}
	if (observer.mLastVersion >= mVersion) {
		return;
	}
	observer.mLastVersion = mVersion;
	observer.mObserver.onChanged((T) mData);
}
Copy the code

The process in the source code is very simple:

  • First by the observermActiveAttribute to determine whether it is active; If the activity continues down, otherwise it’s over.
  • And then through the observershouldBeActive()The method determines if the page is active (for example, if the observer’s page has reached the onDestory function, the page is not active), and if it is not active, it needs to change the state of the observer itself to inactive.
  • If in the active state, then by observing their ownmLastVersionWith LiveDatamVersionFor comparison, ifmLastVersion >= mVersionIs not updated, otherwise is updated.
  • Finally, if there is an update, the observer will modify its ownmLastVersionAnd call theonChanged()Method to update the data.

Third, postValue

static final Object NOT_SET = new Object();
volatile Object mPendingData = NOT_SET;

protected void postValue(T value) {
	boolean postTask;
	synchronized (mDataLock) {
		postTask = mPendingData == NOT_SET;
		mPendingData = value;
	}
	if(! postTask) {return;
	}
	ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

private final Runnable mPostValueRunnable = new Runnable() {
	@SuppressWarnings("unchecked")
	@Override
	public void run(a) {
		Object newValue;
		synchronized(mDataLock) { newValue = mPendingData; mPendingData = NOT_SET; } setValue((T) newValue); }};Copy the code

Here is the source code for postValue: The initial value of mPendingData is NOT_SET. Whenever there is an update, mPendingData records the new value and then posts a Runnable to the main thread. The Runnable internally calls setValue to update the data. And reset the value of mPendingData to NOT_SET.

Compared to setValue, postValue can be called from any thread, while setValue can only be called from the main thread, but postValue is ultimately updated internally by calling setValue.

One caveat to postValue is that when postValue is called multiple times, only the value passed in on the last call is valid. In the postValue source code, mPendingData is used to record the value to be updated, and mPendingData == NOT_SET is used to determine whether to update. However, mPendingData is not equal to NOT_SET before the update is complete, so no new Runnable can be committed. The value of mPendingData can be updated, so the last value passed in is retrieved.

Fourth, the observer. ShouldBeActive ()

In the notice () method mentioned above, we have a shouldBeActive() method that belongs to the observer and should tell if the observer’s page is active. In LiveData, there are two kinds of observers: LifecycleBoundObserver and AlwaysActiveObserver.

4.1 LifecycleBoundObserver and observe

LifecycleBoundObserver is an observer with a judgment about the life cycle of the page in question. It is used in connection with LiveData’s observe() method (observe() calls LifecycleBoundObserver internally). The source code is as follows:

class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {...@Override
	boolean shouldBeActive(a) {
		returnmOwner.getLifecycle().getCurrentState().isAtLeast(STARTED); }... }Copy the code

The LifecycleBoundObserver shouldBeActive() method determines that a page must be at least STARTED to be considered active, An Activity is considered active when it is between onStart() and onPause()**. Here is the status source code:

public enum State {
		// onDestroy()
		DESTROYED,
		INITIALIZED,
		// Between onCreate() -- onStop()
		CREATED,
		// Between onStart() -- onPause()
		STARTED,
		// onResume()
		RESUMED;
	public boolean isAtLeast(@NonNull State state) {}}Copy the code

In summary, the implementation of the shouldBeActive() method in LifecycleBoundObserver gives LiveData the ability to avoid memory leaks.

4.2 AlwaysActiveObserver与observeForever

AlwaysActiveObserver as another observer in LiveData, in conjunction with the observeForever() method (observeForever() calls AlwaysActiveObserver), ShouldBeActive () returns true directly.

private class AlwaysActiveObserver extends ObserverWrapper {...@Override
	boolean shouldBeActive(a) {
		return true; }}Copy the code

In this way, when you subscribe using the observeForever() method, you lose the ability to make lifecycle judgments about the current page; Whenever data changes, observers are notified for update processing. With this approach, you need to be aware of memory leaks.

Five, the summary

This chapter focuses on the internal observer mechanism of LiveData to make it more intuitive to use. In addition to being an important part of the MVVM model, LiveData is now used by many developers as an in-app communication tool. In a future article, we will describe how to use LiveData to build a communication framework.