The foreword 0.

This article is a deep understanding of “Android Architecture Components” series third source based on Android. The arch. The lifecycle: livedata – core: 1.1.1

【AAC Series 1 】 New Era of Android application architecture!

【AAC Series 2 】 A deeper understanding of the building block of architectural components: Lifecycle

[AAC Series 3] In-depth understanding of the architecture component: LiveData

Don’t click this link. Don’t

Before we looked at Lifecycle in depth and at the end of the article we mentioned LiveData and the ViewModel. This time we will talk about LiveData.

LiveData is a member of Android Architecture Components.

LiveData is an observable data holder class. Unlike a regular observable, LiveData is lifecycle-aware, meaning it respects the lifecycle of other app components, such as activities, fragments, or services. This awareness ensures LiveData only updates app component observers that are in an active lifecycle state. [9.1]

This class is designed to hold individual data fields of ViewModel, But can also be used for sharing data between different modules in your application in a (see 9.2).

LiveData is a lifecycle aware, observable data holding class designed as a member variable of the ViewModel. Data can be shared in a more decoupled way.

In practice, LiveData has several features:

  1. The implementation of LiveData is based on the observer pattern;
  2. LiveData is bound to LifecycleOwnerPerceive life cycle changesAnd will only be in LifecycleOwnerActiveState (STARTED/RESUMED) to notify data changes;
  3. LiveData will automatically remove the Observer and unsubscribe from the DESTROYED state, so there is no need to worry about memory leaks.

So how do these LiveData features work? What should I be aware of when using LiveData?

This article will focus on this.

1. Basic usage of LiveData

Although LiveData is usually used in conjunction with the ViewModel, it can also be used on its own and is not used with the ViewModel here for simplicity.

Here’s a simple example:

MutableLiveData<String> liveString = new MutableLiveData<>();
liveString.observe(this.new Observer<String>() {
  @Override
  public void onChanged(@Nullable final String s) {
    Log.d(TAG, "onChanged() called with: s = [" + s + "]"); }}); liveString.postValue("Programs are not apes.");
Copy the code

OnChanged () called with: s = [program is not monkey].

Definition: Define a MutableLiveData (a common subclass of LiveData) that can subscribe to notifications of data changes via the observe method and update data via the postValue() or setValue() methods. Subscribed observers are notified of data changes by calling back to the onChanged() method.

So you’re using LiveData.

Next, the dry stuff!

2. Principle analysis of LiveData

Before analyzing the principle, let’s clarify our questions:

  1. How is LiveData bound to LifecycleOwner to realize life cycle awareness?
  2. How does LiveData only send notifications in the LifecycleOwner active state?
  3. LiveData will automatically unsubscribe in the DESTROY state.
  4. What is the process of updating data through setValue()/postValue()?
  5. What is the data processing process after the lifecycle change?

At the same time, take a look at the LiveData UML diagram I collated in advance to have an overall understanding of LiveData. The following classes involved are all here, which is helpful for understanding.


OK, here we go!

2.1 LiveData. Observe ()

The use process of LiveData starts from Observe (). Let’s try to analyze from observe() method:

    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
        // The state of DESTROYED is ignored
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        // Wrap the Observer in LifecycleBoundObserver
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        // Cache it
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        // If the owner has been observed twice and the owner is different, an error is reported
        if(existing ! =null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if(existing ! =null) {
            return;
        }
        / / bind the owner
        owner.getLifecycle().addObserver(wrapper);
    }
Copy the code

You can see in the observe method that the observer we passed is wrapped in LifecycleBoundObserver, stored in mObservers, and associated with the owner.

And do two special treatment:

  1. Ignore the registration behavior of the owner in DESTROYED;
  2. If an Observer is bound to two owners at the same time, it is regarded as an illegal operation. In other words, an Observer can only be bound to one owner, while an owner can have multiple observers.

There are several new classes LifecycleBoundObserver, ObserverWrapper to take a look.

2.2 LifecycleBoundObserver

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

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

        @Override
        boolean shouldBeActive(a) {
            // Determine whether the current status of the owner is at least STARTED
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        }

        @Override
        public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
            // The life cycle will be DESTROYED automatically if they are DESTROYED
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                removeObserver(mObserver);
                return;
            }
            //ObserverWrapper.activeStateChanged
            activeStateChanged(shouldBeActive());
        }

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

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

**ObserverWrapper **:

    private abstract class ObserverWrapper {
        final Observer<T> mObserver;
        boolean mActive;
        int mLastVersion = START_VERSION;

        ObserverWrapper(Observer<T> observer) {
            mObserver = observer;
        }
				// Whether the status is Active
        abstract boolean shouldBeActive(a);

        boolean isAttachedTo(LifecycleOwner owner) {
            return false;
        }

        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;
            boolean wasInactive = LiveData.this.mActiveCount == 0;
            LiveData.this.mActiveCount += mActive ? 1 : -1;
            if (wasInactive && mActive) {
                onActive();
            }
            if (LiveData.this.mActiveCount == 0 && !mActive) {
                onInactive();
            }
            // In the active state, a data update notification is sent
            if (mActive) {
                dispatchingValue(this); }}}Copy the code

A closer look at these two classes is actually the answer.

LifecycleBoundObserver is a subclass of the abstract ObserverWrapper class, overriding the shouldBeActive() method to consider the owner to be active if the owner is at least in the STARTED state; It also implements the GenericLifecycleObserver interface that listens to the lifecycle callback and handles lifecycle change events in the onStateChanged() method. When an event is received from an DESTROYED, the owner is automatically unbound and the next process is handed over to activeStateChanged().

The questions [2.1] and [2.3] have been answered:

[2.1] A: LifeData wraps the observer with LifecycleBoundObserver in the observe method and binds the owner through it. [2.3] Answer: LifecycleBoundObserver handles life cycle change events in the onStateChanged() method, and will automatically unbind the owner when receiving an DESTROYED event.

Note here that when we call observe() to register, the owner is bound, so in the active case, if there is data in LiveData, the Observer will immediately be notified of the change to that data.

The process is as follows:

observe–>

  onStateChanged–>

    activeStateChanged–>

     dispatchingValue–>

       considerNotify–>

          onChanged

You can call it a lifecycle change-triggered process, and there are two processes that are postValue and SetValue triggered.

2.3 activeStateChanged (Boolean)

In the activeStateChanged() method, the logic associated with onActive() and onInactive() callbacks is handled, and dispatchingValue(this) is called. (MediatorLiveData uses onActive() and onInactive(), which will not be expanded here.)

Let’s explore dispatchingValue.

2.4 dispatchingValue (ObserverWrapper) analysis

    private void dispatchingValue(@Nullable ObserverWrapper initiator) {
        // Return directly if distribution is in progress
        if (mDispatchingValue) {
            // The tag distribution is invalid
            mDispatchInvalidated = true;
            return;
        }
        // Mark the start of distribution
        mDispatchingValue = true;
        do {
            mDispatchInvalidated = false;
            // Life cycle change method initiator is not null
            if(initiator ! =null) {
                considerNotify(initiator);
                initiator = null;
            } else {
                // The postValue/setValue method call passes null initiator
                for (Iterator<Map.Entry<Observer<T>, ObserverWrapper>> iterator =
                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                    considerNotify(iterator.next().getValue());
                    if (mDispatchInvalidated) {
                        break; }}}}while (mDispatchInvalidated);
        // Mark the end of distribution
        mDispatchingValue = false;
    }
Copy the code

ConsiderNotify (ObserverWrapper) method:

    private void considerNotify(ObserverWrapper observer) {
        // Check the status to ensure that it is not distributed to an Inactive observer
        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.
        if(! observer.shouldBeActive()) { observer.activeStateChanged(false);
            return;
        }
        //setValue increases the version. The initial version is -1
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        //noinspection unchecked
        observer.mObserver.onChanged((T) mData);
    }
Copy the code

As you can see, dispatchingValue is the method that handles the dispatch event logic, while the Notice method ensures that only the latest data is dispatched to the Observer in the active state.

You can also see that LiveData has introduced versioning to manage data (mData) to ensure that the data being sent is always up to date. (I won’t go into details)

There are two cases of dispatchingValue:

  1. ObserverWrapper not null
  2. ObserverWrapper null

It needs to be emphasized.

2.4.1 ObserverWrapper Is not NULL

As mentioned above, LifecycleBoundObserver. Call the activeStateChanged onStateChanged method, and the method call dispatchingValue (this); This, LifecycleBoundObserver, is passed in, which is not null.

This is the case for a process triggered by a lifecycle change, in which case only the Observer bound to the Owner is notified.

2.4.2 When ObserverWrapper is NULL

As I mentioned above, in addition to the process triggered by the lifecycle change, there are postValue and SetValue processes. Let’s look at these two methods.

private final Runnable mPostValueRunnable = new Runnable() {
    @Override
    public void run(a) {
        Object newValue;
        synchronized (mDataLock) {
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        //noinspection unchecked
        / / call setValuesetValue((T) newValue); }};protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    if(! postTask) {return;
    }
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

@MainThread
protected void setValue(T value) {
    // Must be called on the main thread otherwise crash will occur
    assertMainThread("setValue");
    mVersion++;// Add the version number
    mData = value;
    // Null is passed
    dispatchingValue(null);
}
Copy the code

The postValue method of LiveData actually posts the operation to the main thread, and the setValue method is called last. Note that setValue must be called from the main thread.

You can also see that the setValue method calls the dispatchingValue method with null and the process notifies the active mObservers**.

All the remaining questions before we get here should be answered.

Both LiveData processes go to dispatchingValue to process the notification logic and determine the owner status before the notification is distributed, plus LiveData’s own internal versioning, Ensures that only the latest data is sent to the Observer in the active state.

** Note: **LiveData has processed the data that is modified multiple times at the same time. If the data is modified multiple times at the same time, only the latest data will be modified.

3. The illustration LiveData

3.1 LiveData class diagram

Look at the class diagram again to review:


3.2 Flowchart of LiveData

Lifecycle changes trigger the process:


Lifecycle postValue/setValue triggers the process:


4. LiveData tips and recipes

There are many other knowledge related to LiveData, some of which are listed here, and more practices can be seen [7.6].

4.1 Sticky Event

When LiveData is subscribed, if the data has changed before and the current owner is active, activeStateChanged() is called and notified immediately to the Observer. This is similar to the sticky Event function of EventBus, but it is important to note that many times we do not need this function. Specific can see [7.6] processing.

4.2 AlwaysActiveObserver

By default, LiveData is bound to LicycleOwner and updates only in the active state. What if you want to be notified of changes to your data in any state? To use AlwaysActiveObserver, call liveData.observeForever (Observer) instead of the observe method.

4.3 MediatorLiveData

Another subclass of LiveData is MediatorLiveData, which allows us to merge multiple LiveData and send notifications if any of them are updated. For example, if we have two data sources, one database and one network, then we have two LiveData sources. In this case, we can use MediatorLiveData to merge the two LiveData sources.

4.4 Transformations

You can take one LiveData and translate it into another one, and map and switchMap are supported, similar to what you can do in RxJava.

For example, map converts a String LiveData to an Integer:

Transformations.map(liveString, new Function<String, Integer>() {
  @Override
  public Integer apply(final String input) {
    return Integer.valueOf(input);
  }
}).observe(this.new Observer<Integer>() {
  @Override
  public void onChanged(@Nullable final Integer integer) {}});Copy the code

4.4 LiveDataBus

EventBus is based on the observer model, as is LiveData, so LiveData can be used to make LiveDataBus, which can be searched if interested.

5. Summary of knowledge

  1. The implementation of LiveData is based on Reactive patterns;
  2. LiveData is bound to LifecycleOwner, which can sense life cycle changes, and only notify data changes when LifecycleOwner is in the Active state (STARTED/RESUMED); If the data changes in the non-active state, the data will change, but no notification will be sent. The notification will be sent after the owner returns to the active state.
  3. If you want to keep receiving notifications, you need to useobserveForever()Methods;
  4. LiveData will automatically remove the Observer and unsubscribe from the DESTROYED state, so there is no need to worry about memory leaks.
  5. While LifecycleOwner is in the DESTROYED state, you cannot subscribe.
  6. The postValue method actually ends up calling setValue but it just puts the operation on the main thread, which is appropriate for an asynchronous thread, setValue must be called on the main thread;
  7. If you call postValue or setValue multiple times to change the data, only the latest data will be changed, that is, you will only receive one notification (set post mixed call is not necessarily);
  8. If LiveData has data and the owner is in the active state, a notification will be received immediately upon subscription;
  9. An Observer instance can only be bound to one LifecycleOwner, while an owner can be bound to multiple Observer instances;
  10. LiveData uses versioning and binding to Lifecycle to ensure that only the latest data is sent to the Observer in the Active state;

6. Summary

LiveData is based on the observer mode and can sense the life cycle, which allows us to use LiveData to enjoy the observer mode brings the powerful decoupling capabilities such as data isolation and UI, but also can enjoy the great convenience brought by the awareness of the life cycle. And there’s no memory leak headache to worry about.

It is very easy to do some very efficient things with LiveData, such as refreshing the UI only in the active state, to avoid unnecessary data refreshes.

It is clear that LiveData has great value in its own right, and it is definitely a great tool in architecture design. In addition, LiveData can be used with ViewModel to play a greater value, and you will know what to talk about in the next article.

7. References and recommendations

  1. LiveData Overview: developer.android.com/topic/libra…
  2. LiveData doc: developer.android.com/reference/a…
  3. Developer.android.com/reference/a…
  4. Developer.android.com/reference/a…
  5. Github.com/googlesampl…
  6. Github.com/googlesampl…