0, preface

For those of you who don’t know much about LiveData and Lifecycle, take a look at my previous article on MVVM based on Android Architecture Components. At the same time, amway’s LiveDataUtils tool library is still being improved. Welcome all star, fork, attention and criticism.

1. WhyLiveDataUsed as an event relay

The advantages (and drawbacks) of using the observer mode to build an EventBus go without saying. When used properly, EventBus and RxBus decouple well, making the architecture clearer and not passing callbacks around. However, they lack the ability to perceive the life cycle of the View layer (activities, fragments, etc.), and need to manually remove the observer at the end of the life cycle. Manual management of the life cycle is very tedious and error-prone. Google’s Lifecycle library is designed to solve this problem. LiveData is a Lifecycle aware observer. Wouldn’t it be nice to build a Lifecycle aware event bus?

2,LiveDataPits used for event passing

With the gradual deepening of the application and understanding of LiveData, especially its “life cycle perception ability”, I gradually found some pits in this way. I would like to take this opportunity to share and discuss with you. In addition, I usually use LiveData purely as event transmission, especially for list operations (such as adding and deleting operations involving IO, View layer needs to know which data is changed, and whether the operation is successful, etc., which can only be transmitted in the form of events).

2.1,postValueData loss problems

As I mentioned in my previous article, you can also look directly at the source code. PostValue simply stores the data passed in to mPendingData, and then throws a Runnable to the main thread. Within this Runnable, setValue is called to actually set the stored value and call back the observers. If the Runnable is postValue multiple times before it is executed, it simply changes the temporary value of mPendingData and does not throw another Runnable. This can cause a problem where the value set later overwrites the previous value, causing the event to be lost.

protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        // Save the data temporarily, the later data will overwrite the previous data
        mPendingData = value;
    }
    // Ensure that only one mPostValueRunnable, #-.- is cast
    if(! postTask) {return;
    }
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
Copy the code

2.2,setValueThe observer is not called back

This is where LiveData’s lifecycle awareness comes in. It does not call back an observer in an “inactive state” (from onStart to onPause), because updating the View at this point is pointless and dangerous, and it waits until the observer is active before calling back the new value. But if I send multiple data (assuming that setValue is guaranteed not to be overwritten), the observers in the inactive state are unaware and will only receive the last data when activated. For event passing, the event is lost, and any data passed in the middle cannot be received, which also loses the significance of event passing.

// The function that LiveData calls when notifying the observer
private void considerNotify(ObserverWrapper observer) {
    // If it is not activated, 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.
    if(! observer.shouldBeActive()) { observer.activeStateChanged(false);
        return;
    }
    // The version is incremented each time the data is updated, so there is no callback for previously sent data
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;
    //noinspection unchecked
    observer.mObserver.onChanged((T) mData);
}
Copy the code

2.3,LiveDataIt’s not intended to deliver events

It can also be seen from the above two points that LiveData (or its observer) does not care how many data changes the middle has undergone before the observer is activated. It will only pass the latest value to an observer when he is activated, and the intermediate value will not take effect. Of course, LiveData is not designed to transmit events, it is designed to reflect the current state of the View, the View layer only needs to render data according to the value of the current LiveData, the View is not visible in the inactive state, even if the update is meaningless. At the beginning, I also thought LiveData used the observer mode and could communicate data between different fragments, so I thought of the event bus. Now I think I was naive at that time.

Of course, this is not to say that LiveData is not dead, with Google providing a powerful Lifecycle library, we can build LiveData that will not lose events.

3, to create a will not lose the eventLiveData

Other functions of LiveData are perfect, but events will be lost. We need to solve the above problems one by one.

3.1,postValueThe problem of

For the postValue problem, since it is also the last call to setValue, we lost data because we only threw a Runable once, so we can solve this problem by throwing a Runable each time to the main thread. For details, see LiveDataUtils.

/** * LiveData related utility class to simplify LiveData operation */
public class LiveDataUtils {
    private static Handler sMainHandler;
    /** * Update MutableLiveData with setValue, if in child thread, switch to main thread */
    public static <T> void setValue(MutableLiveData<T> mld, T d) {
        if (mld == null) {
            return;
        }
        if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
            mld.setValue(d);
        } else{ postSetValue(mld, d); }}/** * Throws SetValueRunnable to the handler of the main thread
    public static <T> void postSetValue(MutableLiveData<T> mld, T d) {
        if (sMainHandler == null) {
            sMainHandler = new Handler(Looper.getMainLooper());
        }
        sMainHandler.post(SetValueRunnable.create(mld, d));
    }

    private static class SetValueRunnable<T> implements Runnable {
        private final MutableLiveData<T> liveData;
        private final T data;

        private SetValueRunnable(@NonNull MutableLiveData<T> liveData, T data) {
            this.liveData = liveData;
            this.data = data;
        }

        @Override
        public void run(a) {
            liveData.setValue(data);
        }

        public static <T> SetValueRunnable<T> create(@NonNull MutableLiveData<T> liveData, T data) {
            return newSetValueRunnable<>(liveData, data); }}}Copy the code

3.2. The problem of inactive state

In fact, I think the main “responsibility” for this problem does not lie in LiveData, but in its observer, “you tell me you are deactivated, how can I send data to you, I send you, in case you have a problem, then who is responsible?” . LifecycleBoundObserver public void Observe (@nonnull LifecycleOwner owner, @nonNULL Observer
Observer will automatically wrap one of these observers for us, and it will be “active” and “inactive” depending on the LifecycleOwner’s life cycle.

// LifecycleBoundObserver
boolean shouldBeActive(a) {
    return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
Copy the code

Public void observeForever(@nonnull Observer
observer), which as the name implies is always active, and LiveData certainly doesn’t manage the lifetime of such an observer for us, Public void removeObserver(@nonnull Final Observer
observer) removes the observer, otherwise memory leaks may occur.

// AlwaysActiveObserver
boolean shouldBeActive(a) {
    return true;
}
Copy the code

The AlwaysActiveObserver seems to solve our problem, it is always active, so all events are called back to it, but we need to manage the life cycle ourselves. Isn’t that turning the clock back on history? You finally have lifecycle awareness, and now you have to do it yourself?

3.3. Create a life-cycle aware observer who does not lose events

Manual management of the lifecycle is absolutely unacceptable, AlwaysActiveObserver can solve this problem, so let’s create a new observer to manage the observeForever and removeObserver problems. If we want to build one, we should build one that works well. First of all, the event must not be lost, or it will be meaningless. And the lifecycle should be managed by the observer itself, not just observeForever and removeObserver, but also inactive states and so on. Now that you want to manage the Lifecycle, you have to use LifecycleOwner, Lifecycle, and then observe LifecycleOwner’s Lifecycle yourself.

/**
 * Marks a class as a LifecycleObserver. It does not have any methods, instead, relies on
 * {@link OnLifecycleEvent} annotated methods.
 * <p>
 * @see Lifecycle Lifecycle - for samples and usage patterns.
 */
@SuppressWarnings("WeakerAccess")
public interface LifecycleObserver {}Copy the code

Lifecycle will only give this interface to Lifecycle and will not have any callbacks. Lifecycle will mark the function with the OnLifecycleEvent annotation that Lifecycle will use to reflect the marked function and generate the corresponding adapter. Lifecycling. GetCallback for those interested. For example, we could use it like this

public class Observer implements LifecycleObserver {
    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    private void onStart(a) { doSomethingOnStart(); }}Copy the code

Once we get the life cycle, we can startobserveForeverIn theLifecycle.Event.ON_DESTROYremoveObserver.

Now we have to worry about active and inactive states, since we haveobserveForever, so each event has a callback, and then ifLifecycleIn the active state, that can send the event directly. But if it’s not activated, if you can’t send it out directly, if you can’t throw it away, then we need to store the event first, and then inLifecycleThe saved event is sent when it becomes active. I drew a simple flow chart.

/** * LiveData acts as an observer during event delivery * ensures that all events are not lost, stores inactive events, and can be called back in active state without memory leaks **@see AsEventBus
 */
public class LiveEventObserver<T> implements LifecycleObserver.Observer<T> {
    private LiveData<T> mLiveData;
    private LifecycleOwner mOwner;
    private Observer<? super T> mObserver;

    private final List<T> mPendingData = new ArrayList<>();

    public LiveEventObserver(LiveData<T> liveData, LifecycleOwner owner, Observer<? super T> observer) {
        mLiveData = liveData;
        mOwner = owner;
        mObserver = observer;
        mOwner.getLifecycle().addObserver(this);
        mLiveData.observeForever(this);
    }

    /** ** may be called at any time before the end of the life cycle
    @Override
    public void onChanged(@Nullable T t) {
        if (isActive()) {
            // If it is active, update it directly
            mObserver.onChanged(t);
        } else {
            // Save the data in the inactive statemPendingData.add(t); }}/ * * *@returnWhether the active state is from onStart to onPause */
    private boolean isActive(a) {
        return mOwner.getLifecycle().getCurrentState()
                .isAtLeast(Lifecycle.State.STARTED);
    }

    /** * onStart is activated. If there is any data stored before, send it */
    @OnLifecycleEvent(Lifecycle.Event.ON_ANY)
    private void onEvent(LifecycleOwner owner, Lifecycle.Event event) {
        if(owner ! = mOwner) {return;
        }
        if (event == Lifecycle.Event.ON_START || event == Lifecycle.Event.ON_RESUME) {
            for (int i = 0; i < mPendingData.size(); i++) { mObserver.onChanged(mPendingData.get(i)); } mPendingData.clear(); }}/** * unobserve and bind all parties and empty data */
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    private void onDestroy(a) {
        mLiveData.removeObserver(this);
        mLiveData = null;

        mOwner.getLifecycle().removeObserver(this);
        mOwner = null;

        mPendingData.clear();

        mObserver = null;
    }


    public static <T> void bind(@NonNull LiveData<T> liveData,
                                @NonNull LifecycleOwner owner,
                                @NonNull Observer<? super T> observer) {
        if (owner.getLifecycle().getCurrentState() == Lifecycle.State.DESTROYED) {
            return;
        }
        newLiveEventObserver<>(liveData, owner, observer); }}Copy the code

3.4 and guaranteeLiveDataEvent update of

Public void observe(@nonnull LifecycleOwner owner, @nonnull Observer
the observer).

/** * for the event bus {@link MutableLiveData}
 *
 * @see AsEventBus
 */
public class EventMutableLiveData<T> extends MutableLiveData<T> {
    @Override
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        LiveEventObserver.bind(this, owner, observer);
    }

    @Override
    public void postValue(T value) {
        LiveDataUtils.setValue(this, value); }}/** * for the event bus {@link MediatorLiveData}
 *
 * @see AsEventBus
 */
public class EventMediatorLiveData<T> extends MediatorLiveData<T> {
    @Override
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        LiveEventObserver.bind(this, owner, observer);
    }

    @Override
    public void postValue(T value) {
        LiveDataUtils.setValue(this, value); }}/** * This annotation is only used for {@linkAndroidx. Lifecycle. LiveData}, is used to represent LiveData be used as an event bus, need to pay attention to: * - Observers in inactive states (after onStart, before onPause) will not generate callbacks, events will be lost * - postValue may be overwritten, only setValue can be used to update values * - LiveData events are sticky, Throw a null event manually when not in use, in case the next binding will send the existing old data; * *@see LiveDataUtils
 * @see LiveEventObserver
 * @see EventMutableLiveData
 * @see EventMediatorLiveData
 */
@Target(ElementType.FIELD)
public @interface AsEventBus {
}
Copy the code

4,LiveDataUtilsIntroduction to other tools in

There are a few other gadgets in the kit that I use most of the time. Here are a few to share:

  • StateDataIs a data wrapper class that contains status and error messages becauseLiveDataThere is only oneonChangedCallback, there is no way to know the state of the data, so we created this class
  • RxLiveDataInherited fromMutableLiveDataTo achieve theDisposableObserverInterface, mainly for data fromRxJavaLiveDataThe transformation of the
  • LiveDataBus, one based onLiveDataEvent bus, but not recommended. The event bus is something you should never use, except when you can’t use it. It smells good when you write it, but it’s a hassle to maintain later

5, summary

I have to say that Google’s life cycle library is really powerful, not only provides us with ready-made tools, but also gives us the ability to facilitate DIY, a less than 500 lines of LiveData can play a lot of tricks. The above is just some of their own experience summary, it is inevitable that there will be deficiencies, welcome criticism and correction.