preface

LiveData component is an observer-based message subscription/distribution component launched by Jetpack. It has host (Activity, Fragment) life-cycle awareness, which ensures that LiveData only distributes messages to active observers. Lifecycle will only receive the message if Lifecycle is in a DESTROYED state. Data listener behavior will be removed automatically if Lifecycle is in a DESTROYED state, thus avoiding common memory leaks and NPE issues.

The message distribution mechanism of LiveData is unmatched by Handler, EventBus and RxjavaBus. They will forward all the messages regardless of whether the current page is visible or not. The result is that the application does useless work to grab resources even when the background page is not visible

Google introduced LiveData

LiveData introduction project

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

Introduction to LiveData core classes and methods

MutableLiveData

You need to use this subclass when you distribute messages using LiveData. The reason for this design is to take into account the single open and close principle, only get the MutableLiveData object can send messages, LiveData object can only receive messages, avoid the chaotic use of both sending and receiving messages when get the LiveData object (design principle: single responsibility)

@SuppressWarnings("WeakerAccess")
public class MutableLiveData<T> extends LiveData<T> {

    public MutableLiveData(T value) {
        super(value);
    }

    public MutableLiveData(a) {
        super(a); }@Override
    public void postValue(T value) {
        super.postValue(value);
    }

    @Override
    public void setValue(T value) {
        super.setValue(value); }}Copy the code

MediatorLiveData

Data transmitted by multiple LiveData can be observed for unified processing. It can also serve as a LiveData observed by other observers

public class MediatorLiveData<T> extends MutableLiveData<T> {
  privateSafeIterableMap<LiveData<? >, Source<? >> mSources =new SafeIterableMap<>();
  
  // Add an observer
  @MainThread
  public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged) {
     // Wrap the observed and observer as a Source object and add it to the map only once
      Source<S> e = newSource<>(source, onChanged); Source<? > existing = mSources.putIfAbsent(source, e);if(existing ! =null&& existing.mObserver ! = onChanged) {throw new IllegalArgumentException(
                  "This source was already added with the different observer");
      }
      if(existing ! =null) {
          return;
      }
      // Check whether the page is active
      if(hasActiveObservers()) { e.plug(); }}@MainThread // Remove observed and remove observer listening
  public <S> void removeSource(@NonNull LiveData<S> toRemote) { Source<? > source = mSources.remove(toRemote);if(source ! =null) { source.unplug(); }}@CallSuper
  @Override // Add observers to all sources
  protected void onActive(a) {
      for(Map.Entry<LiveData<? >, Source<? >> source : mSources) { source.getValue().plug(); }}@CallSuper
  @Override // Active state removes observer listener for all Source
  protected void onInactive(a) {
      for(Map.Entry<LiveData<? >, Source<? >> source : mSources) { source.getValue().unplug(); }}private static class Source<V> implements Observer<V> {
      final LiveData<V> mLiveData;
      final Observer<? super V> mObserver;
      int mVersion = START_VERSION;

      Source(LiveData<V> liveData, final Observer<? super V> observer) {
          mLiveData = liveData;
          mObserver = observer;
      }
      //liveData adds an observer
      void plug(a) {
          mLiveData.observeForever(this);
      }
      //liveData removes the observer
      void unplug(a) {
          mLiveData.removeObserver(this);
      }

      @Override
      public void onChanged(@Nullable V v) {
          if(mVersion ! = mLiveData.getVersion()) { mVersion = mLiveData.getVersion(); mObserver.onChanged(v); }}}Copy the code

Unified observation of data transmitted by multiple LiveData for unified processing

// Create two similar LiveData objects
LiveData<Integer> liveData1 =  new MutableLiveData();
LiveData<Integer> liveData2 = new MutableLiveData();

// Create an aggregate class MediatorLiveData
MediatorLiveData<Integer> liveDataMerger = new MediatorLiveData<>();
// Add the LiveData created above separately.
liveDataMerger.addSource(liveData1, observer);
liveDataMerger.addSource(liveData2, observer);

Observer observer = new Observer<Integer>() {
@Override
public void onChanged(@Nullable Integer s) {
    titleTextView.setText(s);
}
// Once liveData1 or liveData2 sends new data, the Observer can observe it in order to process the UI update uniformly
Copy the code

Transformations

Transformations are used to perform Transformations of the LiveData class. Transformation methods can be used to convey information throughout the lifetime of the observer. The conversion is not evaluated unless the observer observes the returned LiveData object. Because transformations are computed late, lifecycle related behaviors are passed implicitly without the need for other explicit calls or dependencies

Google introduced Transformations

public class Transformations {

    private Transformations(a) {}/**LiveData
      
        userLiveData = ... ; *LiveData
       
         userFullNameLiveData = *Transformations.map( * userLiveData, * user -> user.firstName + user.lastName); }) * /
       
      
    // apply the given function on the main thread to each value emitted by the LiveData source,
    // Then return LiveData to emit the result value. The given function func will be executed on the main thread
    @MainThread
    @NonNull
    public static <X, Y> LiveData<Y> map(
            @NonNull LiveData<X> source,
            @NonNull final Function<X, Y> mapFunction) {
        MapFunction returns a new value set to result
        final MediatorLiveData<Y> result = new MediatorLiveData<>();
        result.addSource(source, new Observer<X>() {
            @Override
            public void onChanged(@Nullable X x) { result.setValue(mapFunction.apply(x)); }});return result;
    }

   
    @MainThread
    @NonNull
    public static <X, Y> LiveData<Y> switchMap(
            @NonNull LiveData<X> source,
            @NonNull final Function<X, LiveData<Y>> switchMapFunction) {
        
         // Listen for source changes to get the value of source
         SwitchMapFunction returns a new LiveData and listens for the value of this LiveData to change set to result
        final MediatorLiveData<Y> result = new MediatorLiveData<>();
        result.addSource(source, new Observer<X>() {
            LiveData<Y> mSource;

            @Override
            public void onChanged(@Nullable X x) {
                LiveData<Y> newLiveData = switchMapFunction.apply(x);
                if (mSource == newLiveData) {
                    return;
                }
                if(mSource ! =null) {
                    result.removeSource(mSource);
                }
                mSource = newLiveData;
                if(mSource ! =null) {
                    result.addSource(mSource, new Observer<Y>() {
                        @Override
                        public void onChanged(@Nullable Y y) { result.setValue(y); }}); }}});return result;
    }

   
    @MainThread
    @NonNull
    public static <X> LiveData<X> distinctUntilChanged(@NonNull LiveData<X> source) {
        // outputLiveData onChanged is triggered only when a different value is changed
        final MediatorLiveData<X> outputLiveData = new MediatorLiveData<>();
        outputLiveData.addSource(source, new Observer<X>() {
            boolean mFirstTime = true;
            @Override
            public void onChanged(X currentValue) {
                final X previousValue = outputLiveData.getValue();
                if (mFirstTime
                        || (previousValue == null&& currentValue ! =null) || (previousValue ! =null && !previousValue.equals(currentValue))) {
                    mFirstTime = false; outputLiveData.setValue(currentValue); }}});returnoutputLiveData; }}Copy the code

You can make a quick buck or a quick buck

class UserViewModel extends AndroidViewModel {
      //mapLiveData<User> userLiveData = ... ;// Return a new LiveData
     LiveData<String> userFullNameLiveData =
          Transformations.map(
                  userLiveData,
                  user -> user.firstName + user.lastName);
      });  
       
     
    //switchMap
    MutableLiveData<String> nameQueryLiveData = ...

    LiveData<List<String>> getUsersWithNameLiveData() {
        return Transformations.switchMap(
            nameQueryLiveData,
                name -> myDataSource.getUsersWithNameLiveData(name));
    }

    void setNameQuery(String name) {
        this.nameQueryLiveData.setValue(name); }}Copy the code

Analysis of implementation principle of LiveData event distribution

LiveData constructor

public abstract class LiveData<T> {
    // Set of observers
    private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
        new SafeIterableMap<>();
    // The event changes the counter
    static final int START_VERSION = -1;
    static final Object NOT_SET = new Object();

    int mActiveCount = 0;
    // Volatile keyword
    private volatile Object mData;
    private int mVersion;

    volatile Object mPendingData = NOT_SET;
   
    public LiveData(T value) {
          mData = value;
          mVersion = START_VERSION + 1;// The event counter increases by 1
    }
   
   public LiveData(a) { mData = NOT_SET; mVersion = START_VERSION; }}Copy the code

Observe Register with host life cycle, no need to register. (Deregister function is called when target life cycle is dead.)

@MainThread // Main thread limit
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    // Assert that this method can only be called on the main thread, as can observeForever.
    assertMainThread("observe");
    // Get the state of the host
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        // ignore
        return;
    }
    // Wrap the registered observer as an observer with a living boundary
    // It can listen for host destruction events, thus actively unregister itself, to avoid memory leaks
    // Whether the observer is active equals whether the host is visible
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    // Can be added only once
    if(existing ! =null && !existing.isAttachedTo(owner)) {
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    if(existing ! =null) {
        return;
    }
    // Add an observer to Lifecycle
    owner.getLifecycle().addObserver(wrapper);
}
Copy the code

LifecycleBoundObserver

Observer object

If you look at the Lifecycle source code, you’ll see that the way observers are added is very similar

// Lifecycle event dispatch interface
public interface LifecycleEventObserver extends LifecycleObserver {
  
    void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event);
}

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

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

    @Override
    boolean shouldBeActive(a) {
        // Observers registered using the Observer method are wrapped as LifecycleBoundObserver
        // Whether the observer is active is equal to whether the host state is greater than or equal to STARTED,
        // If the page is not currently visible and you send a message, it will not be distributed at this time, to avoid background tasks to grab resources, and will be distributed when the page is visible again.
        // Note: If you use observerForever to register an observer,
        // is wrapped as AlwaysActiveObserver, whose shouldBeActive returns true consistently. Data is received even if it is not visible on the page
        return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
    }

    @Override
    public void onStateChanged(@NonNull LifecycleOwner source,
            @NonNull Lifecycle.Event event) {
        // In this case, if the listener detects that the host is destroyed, it actively removes itself from the Observer of LiveData
        if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
            removeObserver(mObserver);// Remove the observer from the map when the host is dead
            return;
        }
        // Indicates that the state of the host has changed, at which point the host is determined to be active
        activeStateChanged(shouldBeActive());
    }

    @Override // Check whether it is the same activity or fragment.
    boolean isAttachedTo(LifecycleOwner owner) {
        return mOwner == owner;
    }

    @Override
    void detachObserver(a) {// This is where the observer is actually called when the map is removed
        mOwner.getLifecycle().removeObserver(this); }}Copy the code

ObserverWrapper

private abstract class ObserverWrapper {
    final Observer<? super T> mObserver;
    boolean mActive;
    int mLastVersion = START_VERSION;//-1 Number of events that are separated

    ObserverWrapper(Observer<? super T> observer) {
        mObserver = observer;
    }

    abstract boolean shouldBeActive(a);

    boolean isAttachedTo(LifecycleOwner owner) {
        return false;
    }

    void detachObserver(a) {}// Host lifecycle changes called
    void activeStateChanged(boolean newActive) {
        if (newActive == mActive) {
            return;
        }
        
        // Change the state of the observer
        mActive = newActive;
        // 0 when the observer is first added
        boolean wasInactive = LiveData.this.mActiveCount == 0;
        OnActive is triggered if there is only one active observer
        LiveData.this.mActiveCount += mActive ? 1 : -1;
        if (wasInactive && mActive) {
            // Some initialization can be done
            onActive();
        }
        // Trigger onInactive if there are no active observers
        // When this method is triggered, you can do many things, such as resource release, etc
        if (LiveData.this.mActiveCount == 0 && !mActive) {
            onInactive();
        }

        // If the observer is active, the data will be distributed
        // Notice that this = observer is passed
        if (mActive) {
            dispatchingValue(this); }}}Copy the code

dispatchingValue

Data distribution process control

//2 Boolean values similar to the flag switch are used to control the sequence control during data distribution
private boolean mDispatchingValue;
private boolean mDispatchInvalidated;

void dispatchingValue(@Nullable ObserverWrapper initiator) {
    if (mDispatchingValue) {//true indicates that the following data distribution process is not complete
        mDispatchInvalidated = true;
        return;
    }
    mDispatchingValue = true;
    do {
        mDispatchInvalidated = false;
        if(initiator ! =null) {
            // The first time the registered observation data is synchronized to the observer
            // The distribution of sticky events allows the first data to receive new data as well as the registered observer
            considerNotify(initiator);
            initiator = null;
        } else {
            for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                    mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                // Non-first-time registered observation data is delivered synchronously to the observer
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {// true indicates that new data is coming and this data delivery is meaningless
                    break; // End the loop}}}}while (mDispatchInvalidated); // Execute
    mDispatchingValue = false;
}
Copy the code

Notice where the data is actually distributed, three conditions need to be met

private void considerNotify(ObserverWrapper observer) {
    // The host is not active
    if(! observer.mActive) {return;
    }
    // Determine that the host is not active and change the observer state to false
    if(! observer.shouldBeActive()) { observer.activeStateChanged(false);
        return;
    }
    // The number of times the observer receives the message is greater than or equal to the number of times the observer sends the message
    // But when the Observer was created, verison=-1
   // If LiveData has already sent data. This is not enough, there is a sticky event, the post-registered observer received the message sent earlier.
    // mLastVersion =-1 mVersion=1 ||>1
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    // Each time a message is distributed, the version of the observer is aligned with that of the LiveData to prevent repeated transmission
    observer.mLastVersion = mVersion;
    // Final data transfer
    observer.mObserver.onChanged((T) mData);
}
Copy the code

SetValue/postValue Event updates are sent

// Can be executed on child threads
protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    if(! postTask) {return;
    }
    // The underlying execution is thrown back to the main thread via handler.post (runnable)
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

// must be executed on the main thread
@MainThread
protected void setValue(T value) {
    assertMainThread("setValue");
    mVersion++; 
    mData = value;
    // Event distribution
    dispatchingValue(null);
}
Copy the code

Other observer: The AlwaysActiveObserver does not care about the host lifecycle state. It can observe data changes at any time. The key is that shouldBeActive returns true at all times and does not check the host lifecycle state

// Regardless of the host lifecycle state, data changes can be observed at any time
@MainThread
public void observeForever(@NonNull Observer<? super T> observer) {
    assertMainThread("observeForever");
    AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
    // Add to observer union
    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// Always return true
    boolean shouldBeActive(a) {
        return true; }}// Remove the observer
@MainThread
public void removeObserver(@NonNull final Observer<? super T> observer) {
    assertMainThread("removeObserver");
    ObserverWrapper removed = mObservers.remove(observer);
    if (removed == null) {
        return;
    }
    removed.detachObserver();// Remove the host's lifecycle observer
    removed.activeStateChanged(false); // Call onInactive () to do some resource release work
}
// Remove all observers from this host
@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

LiveData summary

  • Ensure that the interface conforms to data state LiveData follows observer mode. When the life cycle state changes, LiveData notifies the [Observer] object and sends it the latest data. Instead of updating the interface every time the data changes, the observer can update the interface whenever it receives an onChanged event. (Distribution has logic to check the lifecycle of the host activity.)

  • You don’t need to manually handle the life cycle, you just need to observe the relevant data, you don’t need to manually stop or resume the observation. LiveData automatically manages unregistration of the Observer because it senses changes in the host life cycle and automatically unregisters onDestory in the host life cycle. Therefore, using LiveData for message distribution does not cause memory leaks

  • Data is always up to date. If the host’s life cycle becomes inactive, it receives the latest data when it becomes active again. For example, an Activity that was once in the background receives the latest data as soon as it returns to the foreground.

  • Stickiness event distribution is supported by sending a piece of data and then registering an observer, which by default will receive the previously sent data

  • Shared resources can extend LiveData using the singleton pattern to implement a global message distribution bus. Meituan LiveEventBus