Public number: byte array, hope to help you 🤣🤣

For Android Developer today, Google Jetpack is one of the most fundamental architectural components. Since its launch, it has dramatically changed our development model and made it easier to develop, which requires a certain level of understanding of how some of the sub-components work. So I decided to write a series of articles on Jetpack source code parsing, which I hope will help you 🤣🤣

Series article navigation

  • Jetpack (1) – Lifecycle
  • Jetpack (2) -Lifecycle derivatives
  • Jetpack (3) – LiveData source code in detail
  • Jetpack (4) – LiveData derivatives source code in detail
  • Jetpack (5) – Startup
  • Jetpack (6) – ViewModel
  • Jetpack (7) – SavedStateHandle

LiveData is one of the fundamental components of Jetpack and can be found in many modules. LiveData can be tied to the Lifecycle, data call-back is done when activities and fragments are active, and data listening is automatically removed when activities and fragments are DESTROYED, thus avoiding common memory leaks and NPE issues

This article will introduce the internal implementation of LiveData source code, so that readers can understand its implementation principle and the following important features are easier to ignore:

  • An Observer object can only be bound to a Lifecycle object, otherwise an exception will be thrown
  • The observe and observeForever methods cannot be used in the same Observer. Otherwise, an exception will be thrown
  • There is the possibility of lost value. If the postValue is continuous, only the last value may eventually be retained and called back
  • It is possible that only some observers received callbacks and others did not. When a single thread transmits values continuously or at the same time, for example, valueA and valueB are transmitted successively, only some observers may receive valueA and all observers may receive valueB

The source code described in this article is based on the current latest releases of the following dependency libraries:

    implementation "Androidx. Lifecycle: lifecycle - livedata: 2.2.0." "
Copy the code

A, LiveData

LiveData contains two methods for adding an Observer:

  • observe (LifecycleOwner , Observer)
  • observeForever (Observer)

The only difference between the two approaches is whether they provide a guarantee of life cycle safety

1, observe

LifecycleOwner parameters passed in by the observe(LifecycleOwner, Observer) method mean that Lifecycle objects are carried, and LiveData will determine whether Lifecycle is active, Lifecycle objects are automatically removed when they are in a DESTROYED state. This is an important basis for LiveData to avoid memory leaks

The observe method de-validates an external Observer. If this method has previously been called with the same Observer object and LifecycleOwner is not the same object, an exception is thrown directly, meaning that an Observer is only allowed to bind to a single LifecycleOwner. Because allowing an Observer to bind to multiple lifecycleOwners at the same time can cause LiveData data to change, The RESUMED LifecycleOwner in the RESUMED state and the other LifecycleOwner about to be in a DESTROYED state both received data call-back, which violates the lifecycle safety expected by the Observe method

    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        // Restrict calling the observe method to the main thread only
        assertMainThread("observe");
        Lifecycle is in a DESTROYED state. Observe is meaningless. Lifecycle is in a DESTROYED state
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        // Build a new proxy Observer based on the passed parameters
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        // Store observer as key and wrapper as value
        PutIfAbsent returns NULL when mObservers do not contain the key
        // When mObservers already contain the key, putIfAbsent does not store the key-value and returns the previously saved value
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        if(existing ! =null && !existing.isAttachedTo(owner)) {
            // This step indicates that LiveData already holds an observer, which has been bound to other LifecycleOwner objects
            // Throw an exception directly
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if(existing ! =null) {
            // The Observer has already been passed in with the same owner
            return;
        }
        owner.getLifecycle().addObserver(wrapper);
    }
Copy the code

The above code uses LifecycleBoundObserver, which is the implementation class of the abstract ObserverWrapper class. ObserverWrapper is used to wrap externally passed Observers, define specific abstract methods and common logic for subclasses, and mainly provide common state distribution methods

	private abstract class ObserverWrapper {
    	
    	// An external Observer that listens to LiveData
        final Observer<? super T> mObserver;
    	
    	// Is used to indicate whether the mObserver is active
        boolean mActive;
    
    	// Used to mark the degree of old and new of the last value called back in the Observer
        int mLastVersion = START_VERSION;

        ObserverWrapper(Observer<? super T> observer) {
            mObserver = observer;
        }
		
    	Lifecycle is used to obtain whether Lifecycle is currently active
        abstract boolean shouldBeActive(a);
		
    	Lifecycle is used to determine whether a mObserver is bound to LifecycleOwner
        boolean isAttachedTo(LifecycleOwner owner) {
            return false;
        }
		
    	/ / remove mObserver
        void detachObserver(a) {}void activeStateChanged(boolean newActive) {
            if (newActive == mActive) {
                return;
            }
            // immediately set active state, so we'd never dispatch anything to inactive
            // owner
            mActive = newActive;
            // Check whether all the current LiveData observers are inactive
            boolean wasInactive = LiveData.this.mActiveCount == 0;
            // Updates the number of currently active LiveData observers
            LiveData.this.mActiveCount += mActive ? 1 : -1;
            if (wasInactive && mActive) {
                // If the number of active LiveData observers changes from 0 to 1,
                // Call back the onActive method
                onActive();
            }
            if (LiveData.this.mActiveCount == 0 && !mActive) {
                // If the number of active LiveData observers changes from 1 to 0,
                // calls back the onInactive method
                onInactive();
            }
            if (mActive) {
                // If the mObserver becomes active, the new value is called back to it
                dispatchingValue(this); }}}Copy the code

There are two subclasses of ObserverWrapper: LifecycleBoundObserver and AlwaysActiveObserver, which differ in whether they are bound to the lifecycle

LifecycleBoundObserver also implements the LifecycleEventObserver interface to receive event callbacks for each Lifecycle event switch. The whole event flow is as follows:

  1. Lifecycle changes, calling back onStateChanged method
  2. The onStateChanged method first checks if Lifecycle is in a DESTROYED state and removes the Observer. Otherwise, the callback process continues
  3. OnStateChanged determines whether Lifecycle switches from inactive to active via activeStateChanged and calls dispatchingValue to distribute the value. The dispatchingValue method uses the mLastVersion inside ObserverWrapper to determine if there is a new value that needs to be called back to the external Observer, if so, the new value is called back to it, otherwise the process ends
	class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
        @NonNull
        final LifecycleOwner mOwner;

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

        @Override
        boolean shouldBeActive(a) {
            Lifecycle can only happen if the current state of Lifecycle is STARTED or RESUMED
            // Lifecycle is considered to be active
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        }

    	//LifecycleEventObserver implementation
    	// Lifecycle is called when the Lifecycle state of Lifecycle changes
        @Override
        public void onStateChanged(@NonNull LifecycleOwner source,
                @NonNull Lifecycle.Event event) {
            Lifecycle will remove mObserver if Lifecycle is in a DESTROYED state
            // This is the most important point where LiveData can avoid memory leaks
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                removeObserver(mObserver);
                return;
            }
            activeStateChanged(shouldBeActive());
        }

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

        @Override
        void detachObserver(a) {
            / / remove mObserver
            mOwner.getLifecycle().removeObserver(this); }}Copy the code

2, observeForever

The observeForever method, on the other hand, does not consider the life cycle state of the outside, and calls back whenever the data changes, so it is non-life cycle safe

    @MainThread
    public void observeForever(@NonNull Observer<? super T> observer) {
        // Restrict calling the observe method to the main thread only
        assertMainThread("observeForever");
        AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        if (existing instanceof LiveData.LifecycleBoundObserver) {
            Observe (LifecycleOwner, observer); // Observe (LifecycleOwner, observer)
            // Throw an exception directly here
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if(existing ! =null) {
            // The Observer has already been passed in with the same owner
            return;
        }
        // Actively trigger the activeStateChanged method because LiveData may already be set
        wrapper.activeStateChanged(true);
    }
Copy the code

This code uses AlwaysActiveObserver, which is also an implementation of the abstract ObserverWrapper class. Its shouldBeActive method always returns true, meaning that any data changes should be called back. So the observeForever method is up to the developer to actively remove the Observer to avoid memory leaks and NPE

    private class AlwaysActiveObserver extends ObserverWrapper {

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

        @Override
        boolean shouldBeActive(a) {
            // Make it fixed to return true, which means that the data is called back whenever there are data changes
            return true; }}Copy the code

3, removeObserver

LiveData opens up two methods for adding an Observer, so naturally there will be a removeObserver method

	// Remove the specified Observer
    @MainThread
    public void removeObserver(@NonNull final Observer<? super T> observer) {
        assertMainThread("removeObserver");
        ObserverWrapper removed = mObservers.remove(observer);
        if (removed == null) {
            return;
        }
        removed.detachObserver();
        removed.activeStateChanged(false);
    }

    // Remove all observers bound to a particular LifecycleOwner through a loop
    @SuppressWarnings("WeakerAccess")
    @MainThread
    public void removeObservers(@NonNull final LifecycleOwner owner) {
        assertMainThread("removeObservers");
        for (Map.Entry<Observer<? super T>, ObserverWrapper> entry : mObservers) {
            if(entry.getValue().isAttachedTo(owner)) { removeObserver(entry.getKey()); }}}Copy the code

Update the value

There are two ways to update LiveData values:

  • setValue(T value)
  • postValue(T value)

1, the setValue

The setValue method is restricted to the main thread only

    private volatile Object mData;

    private int mVersion;

	@MainThread
    protected void setValue(T value) {
        assertMainThread("setValue");
    	// Update the version number of the current value
        mVersion++;
        mData = value;
        dispatchingValue(null);
    }
Copy the code

The dispatchingValue() method is cleverly designed. With two global Boolean variables mDispatchingValue and mDispatchInvalidated, the logic of judging the old and new values, discarding the old values and re-issuing the new values globally is realized. Note the traversal process of mObservers. Each traversal of an item checks if the current value is obsolete, and if so, the traversal is interrupted, so that only some observers receive the old value

    // Indicates whether a value is currently in the process of being published to mObservers
	private boolean mDispatchingValue;
	// Is used to flag whether the value currently being published is invalid
	// The new value arrives before the value has been published to all observers, and the old value is invalidated
    @SuppressWarnings("FieldCanBeLocal")
    private boolean mDispatchInvalidated;
	
	If the initiator is null, the mObservers need to be iterated and adjusted
	// If the initiator is not null, only the initiator itself is called back
	@SuppressWarnings("WeakerAccess") /* synthetic access */
    void dispatchingValue(@Nullable ObserverWrapper initiator) {
        if (mDispatchingValue) {
            // If mDispatchingValue is true, it is in the process of publishing mData to mObservers
            // The dispatchingValue method is only called on the main thread, so mDispatchingValue is true
            // Indicates that the onChanged method of the Observer is internally active to LiveData setValue
            "// Set mDispatchInvalidated to true to indicate that a new value is available and the value being called back is obsolete
            mDispatchInvalidated = true;
            return;
        }
        // Used to indicate that mData is in the process of being published to mObservers
        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) {
                        // If mDispatchInvalidated is true, interrupt continues the traversal process
                        // Repeat the loop with the new value
                        break; }}}}while (mDispatchInvalidated);
        mDispatchingValue = false;
    }
Copy the code
    @SuppressWarnings("unchecked")
    private void considerNotify(ObserverWrapper observer) {
        // If the observer is inactive, it returns directly
        if(! observer.mActive) {return;
        }
        // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
        //
        // we still first check observer.active to keep it as the entrance for events. So even if
        // the observer moved to an active state, if we've not received that event, we better not
        // notify for a more predictable notification order.
        // LifecycleBoundObserver
        // Lifecycle may be switched to an inactive State due to Lifecycle, but LifecycleBoundObserver has not received an event notification
        // Therefore, to avoid unexpected situations, check the active status of the Observer actively here and determine whether it needs to update its active status
        if(! observer.shouldBeActive()) { observer.activeStateChanged(false);
            return;
        }
        // Use the value version number mLastVersion in the observer to determine whether a callback is required
        // To avoid repeating callback values to an observer, check here
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        observer.mObserver.onChanged((T) mData);
    }
Copy the code

2, postValue

The postValue method is not limited to the caller’s thread, either the main thread or the child thread can be called, so there is the possibility of multi-thread competition. The key point of the postValue method is to understand the state change between the child thread and the main thread

Since the LiveData callback behavior is fixed to the main thread, the postValue method puts the value callback logic into the Runnable and posts it to the Handler for the main thread to execute. Therefore, there will be a time lag between calling the postValue method and the Runnable being executed, at which point another thread may call the setValue/postValue method and pass the new value

Before mPostValueRunnable is executed, all values passed through the postValue method are saved to the variable mPendingData, and only the last one is kept. MPendingData will not be reset until mPostValueRunnable has been executed, so there is a possibility of losing values (external observers will only receive the latest values) using the postValue method if multiple threads are called simultaneously or if a single thread is called consecutively

   	@SuppressWarnings("WeakerAccess") /* synthetic access */
    final Object mDataLock = new Object();

	@SuppressWarnings("WeakerAccess") /* synthetic access */
	// The default value of mPendingData
	// If mPendingData equals NOT_SET, there is no current LiveData value that needs to be called via postValue
    static final Object NOT_SET = new Object();        

    volatile Object mPendingData = NOT_SET;

	// Used to call back values on the main thread
	private final Runnable mPostValueRunnable = new Runnable() {
        @SuppressWarnings("unchecked")
        @Override
        public void run(a) {
            Object newValue;
            synchronized (mDataLock) {
                // Lock newValue to ensure that it points to the latest value
                newValue = mPendingData;
                / / reset mPendingDatamPendingData = NOT_SET; } setValue((T) newValue); }};protected void postValue(T value) {
        boolean postTask;
        // Lock the mPendingData value to keep it pointing to the latest value
        synchronized (mDataLock) {
            postTask = mPendingData == NOT_SET;
            mPendingData = value;
        }
        // If postTask is false, there is an old value that needs to be called back through postValue
        Since postValue can be called on child threads, the onChanged(value) method of the Observer must be called on the main thread
        // There is a time interval between the child thread and the main thread
        // Wait until mPostValueRunnable is actually executed to send the latest value mPendingData
        if(! postTask) {return;
        }
        // Send a runnable to the main thread, mainly to call postValue in the child thread, and to do a value callback in the main thread
        ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
    }
Copy the code

3. Determine whether the value is new

Let’s take a look at how LiveData determines whether it needs to call back to the Observer. There are two reasons why this judgment is needed instead of a callback every time a new value is received:

  1. observeForeverThe method is to run the callback logic as soon as the value is received, andobserveThe sequence of times in which the method will call back based on Lifecycle changes is uncertain, so it is necessary to determine whether the value being called back is new to the Observer to avoid repeated callbacks
  2. The external may call multiple times at different stagesobserveMethods orobserveForeverMethod, which also needs to be called back only if no value has been passed to the Observer to avoid repeated callbacks

LiveData starts recording old and new values within its constructor, essentially recording the version number of the current value, the degree of old and new, based on an integer mVersion

    static final int START_VERSION = -1;

    private int mVersion;

    /
     * Creates a LiveData initialized with the given {@code value}.
     *
     * @param value initial value
     */
    public LiveData(T value) {
        mData = value;
        mVersion = START_VERSION + 1;
    }

    /
     * Creates a LiveData with no value assigned to it.
     */
    public LiveData(a) {
        mData = NOT_SET;
        mVersion = START_VERSION;
    }
Copy the code

A change in mVersion increases the value by one recursively only when the setValue method receives a new value, indicating that all data within the Observer is out of date and needs to be called back. Since the postValue method still ends up calling the setValue method to do the callback logic, you just need to look at the setValue method

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

When performing a callback to the Observer, check whether value is new to the Observer. If the value is not new, the value is returned directly. If yes, save the version number of the current value mVersion before performing the callback

 	@SuppressWarnings("unchecked")
    private void considerNotify(ObserverWrapper observer) {
        // If the observer is inactive, it returns directly
        if(! observer.mActive) {return;
        }
        // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
        //
        // we still first check observer.active to keep it as the entrance for events. So even if
        // the observer moved to an active state, if we've not received that event, we better not
        // notify for a more predictable notification order.
        // LifecycleBoundObserver
        // Lifecycle may be switched to an inactive State due to Lifecycle, but LifecycleBoundObserver has not received an event notification
        // Therefore, to avoid unexpected situations, check the active status of the Observer actively here and determine whether it needs to update its active status
        if(! observer.shouldBeActive()) { observer.activeStateChanged(false);
            return;
        }
        // Use the value version number mLastVersion in the observer to determine whether a callback is required
        // To avoid repeating callback values to an observer, check here
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        observer.mObserver.onChanged((T) mData);
    }
Copy the code

4. Improve LiveData

LiveData will only distribute events when LifecycleOwner is active. This is an advantage of LiveData, but it can also cause some usage problems. I customized LiveData by copying the source code of LiveData. Design concept and improvement of Jetpack LiveData