Android Jetpack 架构组件(七) 解决LiveData多次发送的粘性事件问题

用过一段时间LiveData就会发现,LiveData会经常多次回调数据。这是因为LiveData存在粘性事件的问题,如果反复注册监听,就会多次回调。

 

下面讲一下如何避免这种情况.

方法一:

尽量避免这种情况。合理的设计activity/fragment的view model。fragment别只绑定activity的view model,要有自己独立的view model, 一一对应,使得livedata和fragment同生共死。做到activity的view model在fragment逻辑上只起到共享数据的作用。

 

方法二:

使用SingleLiveEvent,解决粘性事件的问题。 但是有一个缺点,只能有一个观察者。要考虑自己业务的适用性。

其中的机制是用AtomicBoolean记录setValue状态, 只有当setValue被调用后,才会发送事件。发送完毕后设回false。这样阻止了注册时发送数据。

import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer;

import java.util.concurrent.atomic.AtomicBoolean;

public class SingleLiveEvent extends MutableLiveData {
    private final AtomicBoolean mPending = new AtomicBoolean(false);
    @Override
    public void observe(@NonNull LifecycleOwner owner, @NonNull final Observer observer) {
        super.observe(owner, new Observer() {
            @Override
            public void onChanged(@Nullable T t) {
                if (mPending.compareAndSet(true, false)) {
                    observer.onChanged(t);
                }
            }
        });
    }

    @MainThread
    public void setValue(@Nullable T t) {
        mPending.set(true);
        super.setValue(t);
    }

    /**
     * Used for cases where T is Void, to make calls cleaner.
     */
    @MainThread
    public void call() {
        setValue(null);
    }
}

 

方法三:

使用hook version的方法。这是没有办法的办法。但是功能适配性却最好。没有了粘性事件问题。这也是LiveDataBus源码中的方式。

原理是利用LiveData的mVersion和ObserverWrapper的mLastVersion之间的关系。hook的目的是跳过mVersion和mLastVersion比较这一步,使得onChanged可以执行!

初始注册时mLastVersion是-1, 而LiveData如果之前已经设过值,则mVersion大于-1。所以 mVersion> mLastVersion导致了粘性事件。

    private void considerNotify(ObserverWrapper observer) {
        if (!observer.mActive) {
            return;
        }
      
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }

//hook的目的是跳过这一步,使得onChanged可以执行!
        if (observer.mLastVersion >= mVersion) {
            return;
        }

        observer.mLastVersion = mVersion;
        //noinspection unchecked
        observer.mObserver.onChanged((T) mData);
    }
    public class BusMutableLiveData extends MutableLiveData {

        private Map observerMap = new HashMap<>();

        @Override
        public void observe(@NonNull LifecycleOwner owner, @NonNull Observer observer) {
            super.observe(owner, observer);
            try {
                hook(observer);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        @Override
        public void observeForever(@NonNull Observer observer) {
            if (!observerMap.containsKey(observer)) {
                observerMap.put(observer, new ObserverWrapper(observer));
            }
            super.observeForever(observerMap.get(observer));
        }

        @Override
        public void removeObserver(@NonNull Observer observer) {
            Observer realObserver = null;
            if (observerMap.containsKey(observer)) {
                realObserver = observerMap.remove(observer);
            } else {
                realObserver = observer;
            }
            super.removeObserver(realObserver);
        }

        private void hook(@NonNull Observer observer) throws Exception {
            //get wrapper's version
            Class classLiveData = LiveData.class;
            Field fieldObservers = classLiveData.getDeclaredField("mObservers");
            fieldObservers.setAccessible(true);
            Object objectObservers = fieldObservers.get(this);
            Class classObservers = objectObservers.getClass();
            Method methodGet = classObservers.getDeclaredMethod("get", Object.class);
            methodGet.setAccessible(true);
            Object objectWrapperEntry = methodGet.invoke(objectObservers, observer);
            Object objectWrapper = null;
            if (objectWrapperEntry instanceof Map.Entry) {
                objectWrapper = ((Map.Entry) objectWrapperEntry).getValue();
            }
            if (objectWrapper == null) {
                throw new NullPointerException("Wrapper can not be bull!");
            }
            Class classObserverWrapper = objectWrapper.getClass().getSuperclass();
            Field fieldLastVersion = classObserverWrapper.getDeclaredField("mLastVersion");
            fieldLastVersion.setAccessible(true);
            //get livedata's version
            Field fieldVersion = classLiveData.getDeclaredField("mVersion");
            fieldVersion.setAccessible(true);
            Object objectVersion = fieldVersion.get(this);
            //set wrapper's version
            fieldLastVersion.set(objectWrapper, objectVersion);
        }
    }

方法四:

等官方那个傻逼改正。

你可能感兴趣的:(Android Jetpack 架构组件(七) 解决LiveData多次发送的粘性事件问题)