美文网首页Android-Jetpack
踩坑之路:LiveData之粘性事件

踩坑之路:LiveData之粘性事件

作者: CDF_cc7d | 来源:发表于2019-09-27 19:45 被阅读0次

    前言

    何为粘性事件?
    即发射的事件如果早于注册,那么注册之后依然可以接收到的事件称为粘性事件

    背景

    最近接手了一个公司的项目,采用了目前比较新的技术:LiveData+ViewModel的事件通知框架。该框架拥有大量的优点包括但不仅限于以下:1.实时感知生命周期。2.无需手动回收,解绑,即不会出现内存泄漏的情况。3.数据变化可进行实时通知 等等......
    本人对于这个框架也只是一知半解,奈何项目比较紧急,接手以后马上就要开始干活,所以只能是边干活边了解内部实现原理。
    结果项目中就碰到了一个让我头疼了整整一天的问题。那么到底是啥呢,请接着往下看↓

    起因

    由于公司代码不便放到网上,所以本人便以demo代码代替(主要逻辑一致)

      public class CustomViewModel extends ViewModel {
        MutableLiveData<Integer> mLiveData;
        private int mPostedValue = 10;
        public MutableLiveData<Integer> getLiveData(){
            if(mLiveData == null){
                mLiveData = new MutableLiveData<>();
            }
            loadData();
            return mLiveData;
        }
    
        private void loadData() {
            new Thread(){
                @Override
                public void run() {
                    SystemClock.sleep(2000);
                    mLiveData.postValue(mPostedValue);
                    mPostedValue = mPostedValue * 2;
                }
            }.start();
        }
    }
    

    这个就是ViewModel的代码,用于进行网络请求的操作,返回数据以后通过LiveData实时刷新

    public class MainActivity extends AppCompatActivity {
        private CustomViewModel mViewModel;
        private static final String TAG = "MainActivity";
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mViewModel = ViewModelProviders.of(this).get(CustomViewModel.class);
            final MutableLiveData<Integer> liveData = mViewModel.getLiveData();
            liveData.observe(this, new Observer<Integer>() {
                @Override
                public void onChanged(Integer integer) {
                    Log.e(TAG,"参数返回: " + integer);
                }
            });
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    MutableLiveData<Integer> liveData = mViewModel.getLiveData();
                    liveData.observe(MainActivity.this, new Observer<Integer>() {
                        @Override
                        public void onChanged(Integer integer) {
                            Log.e(TAG,"参数返回: " + integer);
                        }
                    });
                }
            },5000);
        }
    
    }
    

    此为activity里面的代码,onCreate会率先订阅一个LiveData的事件,然后观察网络请求回调。onResume里面的代码模仿的就是点击事件再次进行网络请求。

    现象

    打印结果如下

    2019-09-14 18:48:23.710 6745-6745/com.netease.livedatademo E/MainActivity: 参数返回: 10
    2019-09-14 18:48:26.720 6745-6745/com.netease.livedatademo E/MainActivity: 参数返回: 10
    2019-09-14 18:48:28.721 6745-6745/com.netease.livedatademo E/MainActivity: 参数返回: 20
    2019-09-14 18:48:28.721 6745-6745/com.netease.livedatademo E/MainActivity: 参数返回: 20
    

    首先看到这个结果,肯定不是我想要的结果。下面的20打印了两次,不过这个现象倒是一眼就能看出来,毕竟onCreate的时候已经订阅过一次,你发射的第二次数据自然就会有两个观察者可以监听到。

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mViewModel = ViewModelProviders.of(this).get(CustomViewModel.class);
            final MutableLiveData<Integer> liveData = mViewModel.getLiveData();
            liveData.observe(this, new Observer<Integer>() {
                @Override
                public void onChanged(Integer integer) {
                    Log.e(TAG,"参数返回: " + integer);
                    //解除观察者
                    liveData.removeObserver(this);
                }
            });
        }
    

    所以在onCreate的注册方法里面加上了解除注册的操作。嗯,完美了,打印下看看

    2019-09-14 18:54:17.465 6973-6973/com.netease.livedatademo E/MainActivity: 参数返回: 10
    2019-09-14 18:54:20.483 6973-6973/com.netease.livedatademo E/MainActivity: 参数返回: 10
    2019-09-14 18:54:22.483 6973-6973/com.netease.livedatademo E/MainActivity: 参数返回: 20
    

    事情总是不能如人所愿。。。发现这时候10依然多打了一次,那么问题来了,这个10到底是哪里来的。(我在公司代码里面全局搜索了一遍,发射数据的地方就这么一个,于是就很费解到底怎么回事)

    既然事情结果是这样了,肯定不能就这么下去,于是开始了源码探究,既然onCreate里面的注册已经解除,那么基本没什么必要在往下看下去了,所以打算从onResume调用的注册方法开始下手

        public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
            assertMainThread("observe");
            if (owner.getLifecycle().getCurrentState() == DESTROYED) {
                // ignore
                return;
            }
            LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
            ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
            if (existing != null && !existing.isAttachedTo(owner)) {
                throw new IllegalArgumentException("Cannot add the same observer"
                        + " with different lifecycles");
            }
            if (existing != null) {
                return;
            }
            owner.getLifecycle().addObserver(wrapper);
        }
    

    LiveData在调用Observer以后最后会执行addObserver方法,而此处getLifeCycle获取的对象是LifecycleRegistry对象(原因不过多追究,不是本文重点)

        @Override
        public void addObserver(@NonNull LifecycleObserver observer) {
            ...代码省略...
            while ((statefulObserver.mState.compareTo(targetState) < 0
                    && mObserverMap.contains(observer))) {
                pushParentState(statefulObserver.mState);
                statefulObserver.dispatchEvent(lifecycleOwner, upEvent(statefulObserver.mState));
                popParentState();
                // mState / subling may have been changed recalculate
                targetState = calculateTargetState(observer);
            }
            ...代码省略...
        }
    

    此处会进入while循环,然后调用dispatchEvent方法,所以我们接着往下看

        static class ObserverWithState {
            State mState;
            GenericLifecycleObserver mLifecycleObserver;
    
            ObserverWithState(LifecycleObserver observer, State initialState) {
                mLifecycleObserver = Lifecycling.getCallback(observer);
                mState = initialState;
            }
    
            void dispatchEvent(LifecycleOwner owner, Event event) {
                State newState = getStateAfter(event);
                mState = min(mState, newState);
                mLifecycleObserver.onStateChanged(owner, event);
                mState = newState;
            }
        }
    

    然后会调用LifeCycleBoundObserver的onStateChanged方法

        class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
           
            @Override
            public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
                if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                    removeObserver(mObserver);
                    return;
                }
                activeStateChanged(shouldBeActive());
            }
        }
    
        private abstract class ObserverWrapper {
          
            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();
                }
                if (mActive) {
                    dispatchingValue(this);
                }
            }
        }
    
       void dispatchingValue(@Nullable ObserverWrapper initiator) {
           ...代码省略
            do {
                mDispatchInvalidated = false;
                if (initiator != null) {
                    //会执行此方法
                    considerNotify(initiator);
                    initiator = null;
                } 
            ...代码省略
        }
    
          private void considerNotify(ObserverWrapper 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;
            }
            if (observer.mLastVersion >= mVersion) {
                return;
            }
            observer.mLastVersion = mVersion;
            //noinspection unchecked
            observer.mObserver.onChanged((T) mData);
        }
    

    最终会判断mLastVersion是否比mVersion大,如果小于mVersion,那么会调用onChanged方法,即我们在MainActivity里面注册的事件。那么mLastVersion和mVersion是什么,接着往下看:

        private int mVersion = START_VERSION;
        protected void setValue(T value) {
            assertMainThread("setValue");
            mVersion++;
            mData = value;
            dispatchingValue(null);
        }
    

    我们发现mVersion初始值是-1,这个mVersion是属于LiveData的,然后只在setValue(postValue最终也会调用setValue)的时候会自增1,那么mLastVersion呢?

     int mLastVersion = START_VERSION;
    

    发现mLastVersion也是初始值为-1,而这个mLastVersion是属于ObserverWrapper的,而赋值的地方只有在比较完才会赋值.
    那么仔细回想下,我们其实在onCreate那里注册的观察者信息其实有过一次setValue的操作了,又因为我们其实用的是同一个LiveData,所以mVersion最后是会自增1的,又因为ObserverWrapper在每次注册的时候都会重新new,所以mLastVersion每次都是-1开始。
    那么真相大白了,只要之前有发射过一次数据,那么后面注册的观察者都会接收到之前发射过的数据,而且看样子这个Version值不可以轻易改变,也就是说谷歌不提供API让我们取消掉粘性事件(这算是一个比较大的缺点了)

    解决方案

    问题来了,既然已经知道了原因,那么怎么解决呢?不可能放着不管的。方案有三,容我一一道来


    1.既然每次注册时ObserverWrapper是不一样的,那么只要我们的LiveData也不一样不就可以轻松解决了?

    2.不要多次注册:onCreate里面的注册以后,onResume里面就不要注册了,然后通过判断条件的不同写两个不同的处理方式。

    3.既然无法直接修改mVersion值,和mLastVersion值。那么我们可以直接重写LiveData类。然后重写Observer接口,通过重写的Observer类的onChange方法进行拦截。那么怎么拦截?
    在LiveData的Observe方法里面将传入的Observer对象装饰到我们自己的Observer类里面,然后调用super.Observe的时候将我们自己的Observer方法传入,然后就可以进行自定义拦截。代码如下:

    public class BaseLiveData<T> extends MutableLiveData<T> {
        public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer,boolean isSticky) {
            if(isSticky){
                super.observe(owner, observer);
            } else {
                super.observe(owner,new CustomObserver<T>(observer));
            }
        }
    
        @Override
        public void setValue(T value) {
    
            super.setValue(value);
        }
    
        @Override
        public void postValue(T value) {
            super.postValue(value);
        }
    
        class CustomObserver<T> implements Observer<T> {
            private Observer<? super T> mObserver;
    
            public CustomObserver(Observer<? super T> observer) {
                mObserver = observer;
            }
    
            @Override
            public void onChanged(T t) {
                //此处做拦截操作
                
                mObserver.onChanged(t);
            }
        }
    }
    

    总结

    总的来说解决方案不算复杂,前面两种更是没有什么难度。只不过谷歌不提供API让我们可以解除LiveData的粘性事件确实有点霸道。所以就这点来说远没有EventBust来的灵活,当然LiveData生命感知的能力确实是EventBus无法比拟的。只能说各取所需吧

    相关文章

      网友评论

        本文标题:踩坑之路:LiveData之粘性事件

        本文链接:https://www.haomeiwen.com/subject/fxpayctx.html