What is LiveData? LiveData is one of the components of JetPack. LiveData is an observable data-holding class that is aware of the life cycle. Is an observable data memory class. Unlike regular observable classes, LiveData is lifecycle aware, 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. (From Android official explanation)

The introduction and use of LiveData is not repeated, directly look at the official documents, this article aims to explain the meaning of the existence of LiveData and the principle of implementation. Why did LiveData appear? Look at the explanation of the small column of overweight android before:

LiveData is designed to be responsible only for the distribution of data during the subscriber life cycle, except for setValue/postValue send data and Observe subscribe data. There is no extra way. (From: Restudying the design reasons for Android KunminX-LiveData)

LiveData has the following simple classes:LiveData is all about data distribution, unified data distribution consistency, data distribution awareness lifecycle within the subscriber lifecycle. Google describes the advantages of LiveData as follows:Let’s examine the advantages of LiveData one by one.

postValue/setValue

There are only two methods postValue/setValue for data distribution in LiveData, so what is the difference between these two methods?

PostValue: Can be executed in any thread. SetValue: Can only be executed on the main thread.

 //
      
        Determines the type of data held by LiveData
      
        liveData = MutableLiveData<String>()       
// Set the data to be held
        //postValue can be executed in any thread
        liveData.postValue("1")
        thread {
            liveData.postValue("3")}//setValue can only be executed on the main thread
        liveData.value = "2"
Copy the code

The implementation of the source code in LiveData is as follows: The implementation is very simple, and the setValue is eventually called and stored in mData

    protected void postValue(T value) {
        boolean postTask;
        synchronized (mDataLock) {
            postTask = mPendingData == NOT_SET;//NOT_SET is an empty object
            mPendingData = value;// Store the sent data
        }
        if(! postTask) {// If mPendingData is not equal to NOT_SET, mPostValueRunnable has not been executed yet
            return;
        }
        // Run mPostValueRunnable on the main thread
        ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
    }
	// The main thread executes Runnable
	private final Runnable mPostValueRunnable = new Runnable() {
        @SuppressWarnings("unchecked")
        @Override
        public void run(a) {
            Object newValue;
            synchronized (mDataLock) {
                newValue = mPendingData;
                mPendingData = NOT_SET;// Setting mPendingData to an empty object corresponds to the postTask above
            }
            setValue((T) newValue);// The setValue() method is called after all}};@MainThread // The annotation marks execution on the main thread
    protected void setValue(T value) {
        assertMainThread("setValue"); // Check whether the thread is in the main thread
        mVersion++;// The version number will be used later
        mData = value;//mData Indicates the actual data
        dispatchingValue(null);// Distribute data to observers
    }
Copy the code

Publish/subscribe data

From the postValue/setValue method above, we can see that setValue is eventually called and dispatchingValue is called to publish the data.

Subscribe to LiveData using the observe method:

        // Register subscriber
        //LifecycleOwner AppCompatActivity is implemented
        liveData.observe(this, {
            Log.e("liveData-1"."onCreate: $it")})Copy the code

Let’s first look at how dispatchingValue is implemented for distributing data :(focus first on the code for distributing data)

   void dispatchingValue(@Nullable ObserverWrapper initiator) {
        if (mDispatchingValue) {
            mDispatchInvalidated = true;
            return;
        }
        mDispatchingValue = true;
        do {
            mDispatchInvalidated = false;
            if(initiator ! =null) {//TODO: //TODO: //TODO: //TODO
                considerNotify(initiator);
                initiator = null;
            } else {
                for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {//TODO pays attention to the traversal observer here
                    considerNotify(iterator.next().getValue());//TODO notifies observers that data is coming
                    if (mDispatchInvalidated) {
                        break; }}}}while (mDispatchInvalidated);
        mDispatchingValue = false;
    }
    private void considerNotify(ObserverWrapper observer) {
        // Don't worry about the other code. observer.mObserver.onChanged((T) mData);// Pass the mData to the observer
    }
Copy the code

As shown in the figure below: FinallysetValue throughconsiderNotifyInform subscribers of the data.Now, how does LiveData get throughobserveSubscription data:

    @MainThread Observe observe observe observe observe observe observe observe observe observe observe observe observe observe must be called in the main thread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        assertMainThread("observe");// Check whether it is in the main thread
        // Perceive the life cycle
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        // Perceive the life cycle
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        //TODO stores the observer in mObservers here. Here mObservers, distribute data through the dispatchingValue call
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        if(existing ! =null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if(existing ! =null) {
            return;
        }
        owner.getLifecycle().addObserver(wrapper);
    }
Copy the code

Note: The observer must be called on the main thread.

So overall, the flow chart of LiveData distribution and subscription data is as follows:From the above analysis, the LiveData implementation looks pretty simple, simply traversing the distribution data. If that were the case, LiveData would not necessarily exist and would not reflect the advantages that Google has proposed. Let’s look at the core of LiveData and its advantages: life cycle management and avoiding memory leaks.

Life cycle management

Pass in the JetPack componentLifecycleTo manage the Lifecycle, I’ll do a separate article on Lifecycle. aboutLifecycleSee firstAppCompatActivityTo achieve theLifecycleOwner.AvailablegetLifecycleThrough thegetLifecycle.addObserver()To register life cycle observersThe following code:MyLifeCycleTo achieve theLifecycleEventObserver, life cycle observer

class MyLifeCycle : LifecycleEventObserver {
    override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
        Log.e("TAG"."onStateChanged: " + source.lifecycle.currentState+" event:"+event)
    }
}
Copy the code

To register an observer in an Activity:

        // Be aware of the Activity lifecycle
        lifecycle.addObserver(MyLifeCycle())
Copy the code

So how does LiveData perceive the life cycle? The following code: The principle is the same as above

    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        assertMainThread("observe");// Check whether it is in the main thread
        // Get the state of the current lifecycle if the deStory state is returned directly
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        //LifecycleBoundObserver implements LifecycleEventObserver
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        // Determine whether the owner is the same
        if(existing ! =null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if(existing ! =null) {
            return;
        }
        // Register the observer of the lifecycle owner: Activity/Fragment
        owner.getLifecycle().addObserver(wrapper);
    }
Copy the code

So the key to lifecycle awareness is the implementation of the LifecycleBoundObserver class:


class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
        @NonNull
        final LifecycleOwner mOwner;
    	// The constructor stores the LifecycleOwner and Observer
        LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
            super(observer);
            mOwner = owner;
        }

       // Determine whether the lifecycle is active
        @Override
        boolean shouldBeActive(a) {
            IsAtLeast: compareTo(state) >= 0; In STARTED RESUMED
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        }

       // onStateChanged awareness life cycle
        @Override
        public void onStateChanged(@NonNull LifecycleOwner source,
                @NonNull Lifecycle.Event event) {
            // Get the state of the current lifecycle
            Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
            // Aware of the lifecycle state deStory removed
            if (currentState == DESTROYED) {
                removeObserver(mObserver);// Remove mObserver return from mObservers
                return;
            }
            // Sensed that the state was not deStory
            Lifecycle.State prevState = null;
            while(prevState ! = currentState) { prevState = currentState;// Determine whether it is lifecycle activeactiveStateChanged(shouldBeActive()); currentState = mOwner.getLifecycle().getCurrentState(); }}// Determine whether it is the same LifecycleOwner
        @Override
        boolean isAttachedTo(LifecycleOwner owner) {
            return mOwner == owner;
        }

        // Remove the observer for this lifecycle
        @Override
        void detachObserver(a) {
            mOwner.getLifecycle().removeObserver(this); }}Copy the code

Remove observer:

    @MainThread
    public void removeObserver(@NonNull final Observer<? super T> observer) {
        assertMainThread("removeObserver");
        // Remove the observer of the data
        ObserverWrapper removed = mObservers.remove(observer);
        if (removed == null) {
            return;
        }
        // Log off the observer for the lifecycle
        removed.detachObserver();
        // Reset the status
        removed.activeStateChanged(false);
    }
Copy the code

As you can see from the code above, the LifecycleBoundObserver class acts as a lifecycle observer and has two main methods:

  • shouldBeActiveDetermine whether the student is in the active state, that is, in the: STARTED RESUMED state.
  • onStateChangedSense the life cycle, and if it is perceived to be in a deStory state, executeremoveObserver(also marked with MainThread, which means it must be executed in the MainThread) remove data observers and lifecycle observers. Other states passactiveStateChangedParent class method handling.

LiveData has two kinds of observers: an Observer data Observer and an LifecycleBoundObserver life cycle Observer.

The implementation of activeStateChanged is in the ObserverWrapper parent class:

    private abstract class ObserverWrapper {
        final Observer<? super T> mObserver;/ / store the Observer
        boolean mActive;// The default value is FALSE
        int mLastVersion = START_VERSION;// The latest version will be used later

        ObserverWrapper(Observer<? super T> observer) {
            mObserver = observer;
        }
        / / subclass LifecycleBoundObserver
        abstract boolean shouldBeActive(a);
        // The default return is FALSE. LifecycleBoundObserver has been implemented in subclasses
        boolean isAttachedTo(LifecycleOwner owner) {
            return false;
        }
        // LifecycleBoundObserver has been implemented in subclasses
        void detachObserver(a) {}// Active state change logic
        void activeStateChanged(boolean newActive) {
            if (newActive == mActive) {
                return;
            }
            // immediately set active state, so we'd never dispatch anything to inactive
            // owner
            mActive = newActive;
            changeActiveCounter(mActive ? 1 : -1);
            if (mActive) {
                // In the active state STARTED To dispatch data using dispatchingValue
                dispatchingValue(this); }}}Copy the code

In the above code, activeStateChanged finally determines that mActive is active if TRUE, dispatchingValue is called and ObserverWrapper is passed to distribute the data. Let’s verify the life cycle awareness, the following code: after 5 seconds to send data, we in 5 seconds App into the background, in the foreground, see if we can observe the data.

In theory, when an App enters the background, it will enter the STOPED state and will not notify data observers. When an App enters the foreground and enters the RESUMED state, it will notify observers

        // Registered observer
        //LifecycleOwner AppCompatActivity is implemented
        liveData.observe(this, {
            Log.e("liveData-1"."onCreate: $it")
        })

        liveData.observe(this, {
            Log.e("liveData-2"."onCreate: $it")})// Execute the app delay, enter the background for 10S, and then enter the foreground to view data
        Handler(Looper.getMainLooper()).postDelayed({
            liveData.postValue("lifecyle")},5000)
            
Copy the code

The result is as follows: Registered multiple observers will receive data in the STARTED state

Note: liveData. Observe (…). Each call to Observe generates a LifecycleBoundObserver object that registers the lifecycle observer and is aware of lifecycle changes.

We’ve seen before that dispatchingValue is null when calling setValue and postValue, and ObserverWrapper is passed in lifecycle awareness, so let’s see what’s the difference.

    void dispatchingValue(@Nullable ObserverWrapper initiator) {
        if (mDispatchingValue) {
            mDispatchInvalidated = true;
            return;
        }
        mDispatchingValue = true;
        do {
            mDispatchInvalidated = false;
            if(initiator ! =null) {
                considerNotify(initiator);// Notify the data observer
                initiator = null;
            } else {
                / /... The setValue /postvalue logic is described above}}while (mDispatchInvalidated);
        mDispatchingValue = false;
    }

private void considerNotify(ObserverWrapper observer) {
        // Return if it is not active
        if(! observer.mActive) {return;
        }
        // Check whether it is active again
        if(! observer.shouldBeActive()) { observer.activeStateChanged(false);
            return;
        }
        // If the version of the observer is equal to or greater than the version of the setValue
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        // Unify the version number. The version number is used at the end of the article
        observer.mLastVersion = mVersion;
        // Notify the observer
        observer.mObserver.onChanged((T) mData);
    }
Copy the code

The overall flow chart is as follows:

Viscous event

Through the above analysis, understand the specific implementation of LiveData, there is still a problem, most articles on the Internet said that LiveData support sticky events, then what is sticky events?

Consider: in the following code, call postValue first before calling the observer. Can the observation data be listened to?

 		liveData.postValue("11111")
      // Sticky event liveData also registers observers of the life cycle
        liveData.observe(this, {
            Log.e("liveData-3"."onCreate: $it")})Copy the code

The answer is yes. Why? Look at the code below to register the lifecycle observer with a button and see what prints.

    fun setViscous(view: View) {
        // Re-registering an observer for the lifecycle prints the current lifecycle state
        lifecycle.addObserver(MyLifeCycle())
        // Sticky event liveData also registers observers of the life cycle
        liveData.observe(this, {
            Log.e("liveData-3"."onCreate: $it")})}Copy the code

Look at the result: So, registering the observer of the life cycle again will callonStateChangedWhen registering data observers, colleagues also register life cycle observers, LifecycleBoundObserver is aware of life cycle changes, calls activeStateChanged, is active, The latest mData data is returned to the observer.

The function of the version number in LiveData

What is the use of having an mVersion version number in LiveData and an mLastVersion version number in ObserverWrapper? The change for mVersion is that setValue is only +1 when called

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

For changes in mLastVersion, this only happens when you call Notice to notify the data observer.

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

MVersion > mLastVersion is not notified to subscribers when the setValue has changed. Ensures that LiveData will only notify subscribers if the underlying setValue data changes. Very interesting design, look at the source sure enough can learn a lot of good ideas