美文网首页
JetPack之-LivedataBus

JetPack之-LivedataBus

作者: Coder_Sven | 来源:发表于2020-04-08 18:18 被阅读0次

背景

对于Android系统来说,消息传递是最基本的组件,每一个App内的不同页面,不同组件都在进行消息传递。消息传递既可以用于Android四大组件之间的通信,也可用于异步线程和主线程之间的通信。对于Android开发者来说,经常使用的消息传递方式有很多种,从最早使用的Handler、BroadcastReceiver、接口回调,到近几年流行的通信总线类框架EventBus、RxBus。Android消息传递框架,总在不断的演进之中。

为什么要用LiveDataBus替代EventBus和RxBus

  • LiveDataBus的实现及其简单,相对EventBus复杂的实现,LiveDataBus只需要一个类就可以实现。
  • LiveDataBus可以减小APK包的大小,由于LiveDataBus只依赖Android官方Android Architecture Components组件的LiveData,没有其他依赖,本身实现只有一个类。作为比较,EventBus JAR包大小为57kb,RxBus依赖RxJava和RxAndroid,其中RxJava2包大小2.2MB,RxJava1包大小1.1MB,RxAndroid包大小9kb。使用LiveDataBus可以大大减小APK包的大小。
  • LiveDataBus依赖方支持更好,LiveDataBus只依赖Android官方Android Architecture Components组件的LiveData,相比RxBus依赖的RxJava和RxAndroid,依赖方支持更好。
  • LiveDataBus具有生命周期感知,LiveDataBus具有生命周期感知,在Android系统中使用调用者不需要调用反注册,相比EventBus和RxBus使用更为方便,并且没有内存泄漏风险。

LiveDataBus的组成

  • 消息
    消息可以是任何的Object,可以定义不同类型的消息,如Boolean、String。也可以定义自定义类型的消息。
  • 消息通道
    LiveData扮演了消息通道的角色,不同的消息通道用不同的名字区分,名字是String类型的,可以通过名字获取到一个LiveData消息通道。
  • 消息总线
    消息总线通过单例实现,不同的消息通道存放在一个HashMap中。
  • 订阅
    订阅者通过getChannel获取消息通道,然后调用observe订阅这个通道的消息。
  • 发布
    发布者通过getChannel获取消息通道,然后调用setValue或者postValue发布消息。

LiveDataBus原理图

640.png

LiveDataBus使用示例

public class LiveDataBus {
    //存放订阅者
    private Map<String, MutableLiveData<Object>> bus;

    //单例
    private static LiveDataBus liveDataBus = new LiveDataBus();
    private LiveDataBus(){bus = new HashMap<>();
    }
    public static LiveDataBus getInstance(){return liveDataBus;}

    /**
     * 用来给用户进行订阅(存入map)因为可能会有很多用户进行订阅,所以加上同步
     * @param key
     * @param type
     * @param <T>
     * @return
     */
    public synchronized <T> MutableLiveData<T> with(String key,Class<T> type){
        if(!bus.containsKey(key)){
            bus.put(key,new MutableLiveData<Object>());
        }
        return (MutableLiveData<T>) bus.get(key);
    }

    public MutableLiveData<Object> with(String key) {
        return with(key, Object.class);
    }

}

注册订阅

        //消费者订阅消息
        LiveDataBus.getInstance().with("key",Boolean.class)
                .observe(this, new Observer<Boolean>() {
                    @Override
                    public void onChanged(Boolean bool) {
                       
                            Toast.makeText(MainActivity.this, bool+"", Toast.LENGTH_SHORT).show();
                       
                    }
                });

发送消息

   LiveDataBus.getInstance().with("key",Boolean.class).postValue(true);

问题分析

对于LiveDataBus的第一版实现,我们发现,在使用这个LiveDataBus的过程中,订阅者会收到订阅之前发布的消息。对于一个消息总线来说,这是不可接受的。无论EventBus或者RxBus,订阅方都不会收到订阅之前发出的消息。对于一个消息总线,LiveDataBus必须要解决这个问题。

查看源码步骤分析,订阅者接收到订阅之前发布的消息是因为onChanged()方法被调用了,我们可以从这个方法开始着手查看

 //LiveData.class
 private void considerNotify(ObserverWrapper observer) {
        if (!observer.mActive) {
            return;
        }
        ...
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        //noinspection unchecked
        observer.mObserver.onChanged((T) mData);
    }

从上面代码可以分析出来,如果observer.mLastVersion >= mVersion这个条件成立,那么bserver.mObserver.onChanged((T) mData)就不会调用。我们再来分析observer.mLastVersion和mVersion两个变量我们发现mVersion这个变量是LiveData类的成员变量,初始化值为-1,每当调用一次setvalue()或者postValue()这个值就会自增

    @MainThread
    protected void setValue(T value) {
        assertMainThread("setValue");
        mVersion++;
        mData = value;
        dispatchingValue(null);
    }

ObserverWrapper是观察者的包装类,里面的成员变量mLastVersion初始值也是-1,即每一个新注册的观察者的mLastVersion都是-1

    private abstract class ObserverWrapper {
        final Observer<? super T> mObserver;
        boolean mActive;
        int mLastVersion = START_VERSION;(-1)

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

        abstract boolean shouldBeActive();

        boolean isAttachedTo(LifecycleOwner owner) {
            return false;
        }

        void detachObserver() {
        }

        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);
            }
        }
    }

从上面分析可以得出结论,出现问题原因就是每个新注册的观察者的version小于了Livedata总线的version,即observer.mLastVersion < mVersion,所以会调用 observer.mObserver.onChanged((T) mData)方法,导致新注册的观察者会收到订阅之前的消息。

解决问题

根据上面的分析,我们只需要在注册一个新的订阅者的时候把ObserverWrapper的version设置成跟livedata的version一致就好,那么该如何实现呢。

    @MainThread
    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的observe方法,他会先创建一个LifecycleBoundObserver,LifecycleBoundObserver是ObserverWrapper的子类,然后会把这个LifecycleBoundObserver放入一个私有的map集合mObservers中。无论ObserverWrapper还是LifecycleBoundObserver都是私有的,所以无法通过继承的方式更改LifecycleBoundObserver的version。那么能不能从Map容器mObservers中取到LifecycleBoundObserver,然后再更改version呢?答案是肯定的,通过查看SafeIterableMap的源码我们发现有一个protected的get方法。因此,在调用observe的时候,我们可以通过反射拿到LifecycleBoundObserver,再把LifecycleBoundObserver的version设置成和LiveData一致即可。

代码最终实现

public class LiveDataBus {
    //存放订阅者
    private Map<String, BusMutableLiveData<Object>> bus;

    //单例
    private static LiveDataBus liveDataBus = new LiveDataBus();

    private LiveDataBus() {
        bus = new HashMap<>();
    }

    public static LiveDataBus getInstance() {
        return liveDataBus;
    }

    /**
     * 用来给用户进行订阅(存入map)因为可能会有很多用户进行订阅,所以加上同步
     *
     * @param key
     * @param type
     * @param <T>
     * @return
     */
    public synchronized <T> BusMutableLiveData<T> with(String key, Class<T> type) {
        if (!bus.containsKey(key)) {
            bus.put(key, new BusMutableLiveData<Object>());
        }
        return (BusMutableLiveData<T>) bus.get(key);
    }

    public BusMutableLiveData<Object> with(String key) {
        return with(key, Object.class);
    }

    public static class BusMutableLiveData<T> extends MutableLiveData<T> {
        @Override
        public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
            super.observe(owner, observer);
            hook(observer);
        }

        private void hook(Observer<? super T> observer) {

            //通过反射修改mLastVersion的值
            Class<LiveData> liveDataClass = LiveData.class;
            try {
                Field field = liveDataClass.getDeclaredField("mObservers");
                field.setAccessible(true);
                //获取这个字段对应的对象
                Object mObject = field.get(this);
                //得到SafeIterableMap.class
                Class<?> aClass = mObject.getClass();
                //获取到SafeIterableMap的get方法
                Method get = aClass.getDeclaredMethod("get", Object.class);
                get.setAccessible(true);
                //执行get方法
                Object invoke = get.invoke(mObject, observer);
                //取到SafeIterableMap中的value
                Object observerWraper = null;
                if (invoke != null && invoke instanceof Map.Entry) {
                    observerWraper = ((Map.Entry) invoke).getValue();
                }
                if (observerWraper == null) {
                    throw new NullPointerException("observerWraper is null");
                }
                //得到ObserverWrapper的类对象(这里需要getSuperclass是因为map集合塞的就是ObserverWrapper的子类对象)
                Class<?> superclass = observerWraper.getClass().getSuperclass();
                Field mLastVersion = superclass.getDeclaredField("mLastVersion");
                mLastVersion.setAccessible(true);

                //得到mVersion
                Field mVersion = liveDataClass.getDeclaredField("mVersion");
                mVersion.setAccessible(true);
                //把mVersion的值填入到mLastVersion中去
                Object mVersionValue = mVersion.get(this);
                mLastVersion.set(observerWraper, mVersionValue);

            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }
}

源码地址:https://github.com/games2sven/JetPack_LiveDataBus

参考了美团技术团队的博客,博客地址:https://www.cnblogs.com/meituantech/p/9376449.html

相关文章

网友评论

      本文标题:JetPack之-LivedataBus

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