Jetpack LiveData source code analysis

1, LiveData overview

LiveData is an observable data holder class. LiveData is located under the android.lifecycle package and has the ability to sense the life cycle, such as the life cycle of Activity, Fragment, Service, etc. This perception means that there is no need to manually handle the life cycle when using, so as to avoid problems such as memory leakage.

LiveData uses the Observer mode. When the data changes, LiveData will notify the Observer to complete the interface update, that is, the data-driven UI. If an Observer's lifecycle is in the started or RESUME state, LiveData considers the Observer active.

2, Start with use  

The use of LiveData is very simple. Create the info data and Observer object saved by LiveData, bind the Observer object to LiveData using the observe() method, and the onChanged method of Observer will update the Textview when the data changes.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    val textView : TextView = findViewById(R.id.tv_textview)

    MyLiveData.info.observe(this, object: Observer<String> {
        override fun onChanged(t: String?) {
            textView.text = t // Update TextView
        }
    })

    MyLiveData.info.observeForever(object: Observer<String> {
        override fun onChanged(t: String?) {
            textView.text = t // Update TextView
        }
    })
    //Change data
    MyLiveData.info.value = "Main thread modification"
    thread {
        Thread.sleep(3000)
        MyLiveData.info.postValue("Child thread modification")
    }
}

object MyLiveData { 
    val info: MutableLiveData<String> by lazy { MutableLiveData() }
}

You can see that LiveData has two methods to bind the Observer object: the observe() method and the observeForever() method. What's the difference between the two?

observe

The observe() method additionally passes in this, that is, Lifecycle owner, because the parent class implements the Lifecycle owner interface in AppCompatActivity. This LifecycleOwner parameter is equivalent to Lifecycle and is the key to sensing the Lifecycle. The observe() method will process it internally according to the Lifecycle state. When Lifecycle is active, it will call back to the onChanged method. On the contrary, it will not do anything.

    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        assertMainThread("observe");
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            return;
        }
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        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);
    }

As can be seen from the above code, some stability judgments are made first, and the Observer is only added once, that is, an Observer is only allowed to bind to one lifecycle owner. Then, the lifecycle owner and Observer are repackaged with a layer of LifecycleBoundObserver, and then added to the LifecycleRegistry. The related LifecycleRegistry can be viewed Previous Lifecycle article . Next, take a look at lifecycle bundobserver

    class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
        @NonNull
        final LifecycleOwner mOwner;

        LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
            super(observer);
            mOwner = owner;
        }

        @Override
        boolean shouldBeActive() {
            //Returns true when Lifecycle is in the STARTED or RESUMED state
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        }

        @Override
        public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
            //When DESTROYED, the mObserver is removed
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                removeObserver(mObserver);
                return;
            }
            activeStateChanged(shouldBeActive());
        }

        @Override
        boolean isAttachedTo(LifecycleOwner owner) {
            return mOwner == owner;
        }

        @Override
        void detachObserver() {
            mOwner.getLifecycle().removeObserver(this);
        }
    }

LifecycleBoundObserver indirectly implements LifecycleObserver, so the onStateChange() method will be called back when the life cycle changes. In this method, when it is judged that the host life cycle is DESTROYED, the mObserver will be removed, which does not need to be removed manually by the user; In other states, the activeStateChanged method of the parent class ObserverWrapper will be called. If the life cycle is active, the dispatchingValue method will be called. This method will be analyzed later by setValue.

        void activeStateChanged(boolean newActive) {
            if (newActive == mActive) {
                return;
            }
            ...
            //Note that the parameter here is this
            if (mActive) {
                dispatchingValue(this);
            }
        }

observeForever

The observeForever() method is not associated with a lifecycle owner, and the Observer will be regarded as always active. Therefore, when the lifecycle is inactive, it will also receive a notification, and there will be a risk of memory leakage. This is a bit like a Handler. Using observeForever() also requires a manual call to RemoveObserver() to remove the Observer. Unlike the Observer method, the framework has handled it automatically.

    public void observeForever(@NonNull Observer<? super T> observer) {
        assertMainThread("observeForever");
        AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        if (existing != null && existing instanceof LiveData.LifecycleBoundObserver) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if (existing != null) {
            return;
        }
        wrapper.activeStateChanged(true);
    }
    private class AlwaysActiveObserver extends ObserverWrapper {

        AlwaysActiveObserver(Observer<? super T> observer) {
            super(observer);
        }

        @Override
        boolean shouldBeActive() {
            return true;
        }
    }

The above code uses alwaysactivetobserver to wrap the Observer. Like LifecycleBoundObserver, it is the implementation class of ObserverWrapper, but the shouldBeActive() method directly returns true and there is no lifecycle change monitoring, so the callback will be executed as long as the data changes.

3, Data update

LiveData does not disclose the method of updating data. We generally use its subclass MutableLiveData, which provides two setValue() and postValue() methods to shield other implementation details. Generally, setValue() is used in the main thread and postValue() is used in the child thread. Calling these two methods will trigger onChange() of the observer.

setValue

As you can see from setValue() in the LiveData class, this method can only be called in the main thread. Every time the new value is set, the version number mVersion++ is recorded, and then the dispatchingValue() is called.

    @MainThread
    protected void setValue(T value) {
        assertMainThread("setValue");
        mVersion++;
        mData = value;
        //Note that null is passed in here
        dispatchingValue(null);
    }

mDispatchingValue indicates that the new value is being distributed to the Observer, and mDispatchInvalidated indicates whether the new value is invalid. These two Boolean values realize the logic of judging the old and new values. Because the initiator passed in above is null, it will go to else. Here, it will traverse all registered observers and send values one by one. When setValue is called externally to send a new value before the value has been distributed, the undistributed value will be set as invalid and the new value will be redistributed. Let's take a look at the last step, considerNotify()

    void dispatchingValue(@Nullable ObserverWrapper initiator) {
        if (mDispatchingValue) {
            mDispatchInvalidated = true;
            return;
        }
        //Distributing new values
        mDispatchingValue = true;
        do {
            mDispatchInvalidated = false;
            if (initiator != null) {
                considerNotify(initiator);
                initiator = null;
            } else {
                //Come here
                for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                    considerNotify(iterator.next().getValue());
                    if (mDispatchInvalidated) {
                        //Invalid value discard
                        break;
                    }
                }
            }
        } while (mDispatchInvalidated);
        //End of new value distribution
        mDispatchingValue = false;
    }

You can see that basically, each method will judge whether the Lifecycle is active or not, and obtain the Lifecycle lifecycle from time to time to get the latest status. The mVersion here is set to increment in setValue(), and the corresponding value changes from the default - 1 to 0. The mLastVersion is used to mark the version number of the last callback value in the Observer. The default value is - 1, so it does not meet the if judgment. Go down and call the sonChanged() method. The process ends here. Normally, the mVersion value is greater than mLastVersion. In order to prevent repeated distribution of values to an Observer, this judgment is added.

Using this judgment, we can deal with the sticky events of LiveData. Sticky events can be sent first and then subscribed. LiveData supports sticky event delivery by default. In many cases, we don't need sticky events. Modify the value of mLastVersion and mVersion by reflection, so that the code does not go down, that is, the sticky event can be closed without distributing the old value.

    private void considerNotify(ObserverWrapper observer) {
        //Determine whether it is active
        if (!observer.mActive) {
            return;
        }
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        //onChanged method to notify the observer
        observer.mObserver.onChanged((T) mData);
    }

postValue

postValue() can be invoked in the sub thread, so a lock is added to the source code to prevent multithreaded competition. Its main realization is to switch to the main thread through Handle, and to call the setValue() method to distribute data in Runnable, leaving the same logic as setValue().

    protected void postValue(T value) {
        boolean postTask;
        synchronized (mDataLock) {
            postTask = mPendingData == NOT_SET;
            mPendingData = value;
        }
        if (!postTask) {
            return;
        }
        //The Handler switches to the main thread for execution
        ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
    }

    private final Runnable mPostValueRunnable = new Runnable() {
        @Override
        public void run() {
            Object newValue;
            synchronized (mDataLock) {
                newValue = mPendingData;
                mPendingData = NOT_SET;
            }
            //noinspection unchecked
            setValue((T) newValue);
        }
    };

4, Summary

1. LiveData mainly senses the life cycle through Lifecycle;

2. LiveData will only notify the observers whose life cycle is in started or RESUME status of the update;

3. The LiveData update notification callback is executed in the main thread, so you can only add an Observer in the main thread;

3. Both LiveData class and ObserverWrapper class will hold a version number of value, and the data will only be distributed to active observers with different version numbers;

Tags: Android Android Studio kotlin

Posted on Sat, 20 Nov 2021 19:29:40 -0500 by kkeim