美文网首页
解决 LiveData 粘性事件问题

解决 LiveData 粘性事件问题

作者: 李云龙_ | 来源:发表于2022-09-16 06:30 被阅读0次

    解决 LiveData 粘性事件问题

    1. 什么是粘性事件

    从上一个 Activity 发送数据后,在下一个 Activity 注册 observer 还能收到,那么这种事件就叫做粘性事件(即允许先发送再注册就是粘性事件)。

    2. observer.mLastVersion 和 mVersion 的关系

    谁的 version 最大谁的数据就是最新的, 并且有 setValue() 会更新 mData 的值, 同时让 mVersion + 1, 那么有如下几种情况 :

    1. 如果 observer 的 mLastVersion 大, 那么 observer 的数据是最新的, 直接返回

    2. 如果 observer 的 mLastVersion 和 mVersion 一样大, 那么说明 observer 的数据 和 mData 的数据一样新, 直接返回

    3. 如果 observer 的 mLastVersion 比 mVersion 小, 那么说明 mData 的数据是最新的, 需要更新 ui, 同时重置 observer 的 mLastVersion 为 mVersion (因为此时数据一样新了)

    3. 解决 LiveData 粘性事件的思路

    使用反射让 observer.mLastVersion == mVersion

    源码解析
    public abstract class LiveData<T> {
    
        private void considerNotify(ObserverWrapper observer) {
            //...
            //observer 的 mLastVersion 比 mVersion 大 (observer 的数据是最新的)
            //observer 的 mLastVersion 和 mVersion 一样大 (observer 的数据 和 mData 的数据一样新)
            if (observer.mLastVersion >= mVersion) {
                return;
            }
    
            //mData 的数据是 最新的
            observer.mLastVersion = mVersion;
            //noinspection unchecked
            //观察者就是 observer.mObserver, 调用其 onChanged() 方法
            observer.mObserver.onChanged((T) mData);
        }
    }
    
    4. 实例代码

    在 LiveDataBus 中重写 MutableLiveData 类,并将之前的 MutableLiveData 替换为 BusMutableLiveData :

    public class LiveDataBus {
    
        private static LiveDataBus liveDataBus = new LiveDataBus();
        private Map<String, BusMutableLiveData<Object>> map;    //存储所有 LiveData 的容器
    
        private LiveDataBus(){
            map = new HashMap<>();
        }
    
        public static LiveDataBus getInstance(){
            return liveDataBus;
        }
    
        /**
         * 存和取一体的方法, 这里传入了 type 却又不使用, 是因为要使用泛型 T 而不是 type
         * @param key
         * @param type
         * @param <T>
         * @return
         */
        public synchronized<T> BusMutableLiveData<T> with(String key,Class<T> type){
            if(!map.containsKey(key)){
                map.put(key,new BusMutableLiveData<Object>());
            }
            return (BusMutableLiveData<T>) map.get(key);
        }
    
        public class BusMutableLiveData<T> extends MutableLiveData<T> {
            //false;  需要粘性事件   true  不需要粘性事件
            private boolean isViscosity = false;
    
            public void observe(@NonNull LifecycleOwner owner, boolean isViscosity, @NonNull Observer<T> observer) {
                this.isViscosity = isViscosity;
                observe(owner, observer);
            }
    
            @Override
            public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
                super.observe(owner, observer);
                try {
                    if(isViscosity){
                        //通过反射  获取到mVersion  获取到mLastVersion  将mVersion 的值给mLastVersion
                        hook(observer);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
    
    
            /**
             * hook技术的实现方法  拦截onChange方法的执行
             * @param observer
             */
            private void hook(Observer<? super T> observer) throws Exception {
                //获取到LiveData的类对象
                Class<LiveData> liveDataClass = LiveData.class;
                //根据类对象获取到mVersion的反射对象
                Field mVersionField = liveDataClass.getDeclaredField("mVersion");
                //打开权限
                mVersionField.setAccessible(true);
                //获取到mObservers的反射对象
                Field mObserversField = liveDataClass.getDeclaredField("mObservers");
                //打开权限
                mObserversField.setAccessible(true);
                //从当前的LiveData对象中获取mObservers这个成员变量在当前对象中的值
                Object mObservers = mObserversField.get(this);
                //获取到mObservers这个map的get方法
                Method get = mObservers.getClass().getDeclaredMethod("get", Object.class);
                //打开权限
                get.setAccessible(true);
                // 执行get方法
                Object invokeEntry = get.invoke(mObservers, observer);
                //定义一个空对象   LifecycleBoundObserver
                Object observerWrapper = null;
                if(invokeEntry !=null && invokeEntry instanceof Map.Entry){
                    observerWrapper = ((Map.Entry)invokeEntry).getValue();
                }
                if(observerWrapper == null){
                    throw new NullPointerException("ObserverWrapper不能为空");
                }
    
                //得到observerWrapper的类对象
                Class<?> aClass = observerWrapper.getClass().getSuperclass();
                //获取mLastVersion的发射对象
                Field mLastVersionField = aClass.getDeclaredField("mLastVersion");
                //打开权限
                mLastVersionField.setAccessible(true);
                //获取到mVersion的值
                Object o = mVersionField.get(this);
                //把它的值赋值给mLastVersion
                mLastVersionField.set(observerWrapper,o);
            }
        }
    }
    

    LoginActivity 中将 MutableLiveData 替换为 LiveDataBus.BusMutableLiveData<String> :

    @BindPath("login/login")
    public class LoginActivity extends AppCompatActivity {
        
        LiveDataBus.BusMutableLiveData<String> liveData;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_login);
    
            liveData = LiveDataBus.getInstance().with("lisan", String.class);
       
            //设置为 true 表示不接收粘性事件
            this.liveData.observe(LoginActivity.this, true, new Observer<String>() {
                @Override
                public void onChanged(String s) {
                    Log.e("LoginActivity-->",s);
                }
            });
        }
    
        public void clickSendData(View view){
            liveData.postValue("LoginActivity clickSendData");
        }
    }
    

    此时从 MainActivity 跳转到 LoginActivity 后,LoginActivity 并不会触发 observer 的 onChanged() 方法,但是点击调用 clickSendData() 方法后,仍然可以触发 observer 的 onChanged() 方法,即使用 LiveDataBus.BusMutableLiveData<String> 并设置 isViscosity = true 并不会妨碍后边的消息接收。

    5. LiveDataBus 使用一种 key 创建 liveData 对象导致的类型转换异常

    在 MainActivity 中使用 LiveDataBus 根据 key="lisan" 创建一个泛型为 String 的 liveData 对象, 然后使用 liveData.postValue("MainActivity postValue"); 发送一个字符串 :

    @BindPath("main/main")
    public class MainActivity extends AppCompatActivity {
        MutableLiveData<String> liveData;
    
         @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            liveData = LiveDataBus.getInstance().with("lisan", String.class);
            liveData.postValue("MainActivity postValue");
        }
    }
    

    但是在 LoginActivity 中根据同样的 key 创建一个 泛型为 User 的 liveData 对象

    @BindPath("login/login")
    public class LoginActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
    
            LiveDataBus.BusMutableLiveData<User> lisan = LiveDataBus.getInstance().with("lisan", User.class);
            lisan.observe(LoginActivity.this, false, new Observer<User>() {
                @Override
                public void onChanged(User user) {
                    Log.e("LoginActivity-->", user.getName());
                }
            });
        }
    }
    

    由于设置粘性事件为 false (需要粘性事件),导致运行时报异常 :

        java.lang.ClassCastException: java.lang.String cannot be cast to com.maniu.login.User
            at com.maniu.login.LoginActivity$1.onChanged(LoginActivity.java:34)
            at androidx.lifecycle.LiveData.considerNotify(LiveData.java:113)
            at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:126)
            at androidx.lifecycle.LiveData$ObserverWrapper.activeStateChanged(LiveData.java:424)
            at androidx.lifecycle.LiveData$LifecycleBoundObserver.onStateChanged(LiveData.java:376)
    

    因为 this.mData 是 MainActivity 中设置的 String 对象,当调用 observer.mObserver.onChanged(this.mData) 将 String 对象强制转换为 User 时就会报异常。

    public abstract class LiveData<T> {
        //...
        private void considerNotify(LiveData<T>.ObserverWrapper observer) {
            if (observer.mActive) {
                if (!observer.shouldBeActive()) {
                    observer.activeStateChanged(false);
                } else if (observer.mLastVersion < this.mVersion) {
                    observer.mLastVersion = this.mVersion;
                    observer.mObserver.onChanged(this.mData);
                }
            }
        }
    }
    
    public interface Observer<T> {
        void onChanged(T var1);
    }
    

    相关文章

      网友评论

          本文标题:解决 LiveData 粘性事件问题

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