还在用RxBus&EventBus?你Out啦!哥哥教你用更简单的LiveData来代替Android事件总线

首先我们来看消息总线的进化:

消息传递既可以用于Android四大组件之间的通信,也可用于异步线程和主线程之间的通信,所以这是Android开发中极为重要的一环。

从最早的Intent,Handler,BroadcastReceiver,接口回调,到最近几年开发者中流行的通信总线类的框架EventBus,RxBus。我们发现Android消息传递框架机制,总在不断进化中……

我们从EventBus说起:

EventBus是一个Android事件发布/订阅框架,通过解耦发布者和订阅者简化Android事件传递。

还在用RxBus&EventBus?你Out啦!哥哥教你用更简单的LiveData来代替Android事件总线_第1张图片
EventBus给Android世界带来了新思想

RxBus出现:

趁着响应式编程的风,RxBus其实本身不需要过多的分析,其强大完全依赖于RxJava技术,RxJava天生就是发布/订阅模式,而且很容易线程切换,所以RxBus凭借区区30行代码,就打败了EventBus统治多年的老大地位。

原理:

在RxJava中有个Subject类,它继承Observable类,同时实现了Observer接口,因此Subject可以同时担当订阅者和被订阅者的角色,我们使用Subject的子类PublishSubject来创建一个Subject对象(PublishSubject只有被订阅后才会把接收到的事件立刻发送给订阅者),在需要接收事件的地方,订阅该Subject对象,之后如果Subject对象接收到事件,则会发射给该订阅者,此时Subject对象充当被订阅者的角色。

完成了订阅,在需要发送事件的地方将事件发送给之前被订阅的Subject对象,则此时Subject对象作为订阅者接收事件,然后会立刻将事件转发给订阅该Subject对象的订阅者,以便订阅者处理相应事件,到这里就完成了事件的发送与处理。

最后就是取消订阅的操作了,RxJava中,订阅操作会返回一个Subscription对象,以便在合适的时机取消订阅,防止内存泄漏,如果一个类产生多个Subscription对象,我们可以用一个CompositeSubscription存储起来,以进行批量的取消订阅。

RxBus两种实现:

AndroidKnife/RxBus(https://github.com/AndroidKnife/RxBus)

Blankj/RxBus(https://github.com/Blankj/RxBus)

基于Rxjava1的RxBus实现:

public finalclassRxBus{

privatefinalSubject bus;

privateRxBus(){

bus =newSerializedSubject<>(PublishSubject.create());

}

privatestaticclassSingletonHolder{

privatestaticfinalRxBus defaultRxBus =newRxBus();

}

publicstaticRxBusgetInstance(){

returnSingletonHolder.defaultRxBus;

}

/*

     * 发送

     */

publicvoidpost(Object o){

bus.onNext(o);

}

/*

     * 是否有Observable订阅

     */

publicbooleanhasObservable(){

returnbus.hasObservers();

}

/*

     * 转换为特定类型的Obserbale

     */

publicObservable toObservable(Class type){

returnbus.ofType(type);

}

}

基于RxJava2的RxBus实现:

public finalclassRxBus2{

    private final Subject bus;

    private RxBus2() {

        // toSerialized method made bus thread safe

        bus = PublishSubject.create().toSerialized();

    }

publicstaticRxBus2getInstance(){

        return Holder.BUS;

    }

privatestaticclassHolder{

        private static final RxBus2 BUS = new RxBus2();

    }

publicvoidpost(Objectobj){

        bus.onNext(obj);

    }

publicObservabletoObservable(Class tClass){

        return bus.ofType(tClass);

    }

publicObservabletoObservable(){

        return bus;

    }

publicbooleanhasObservers(){

        return bus.hasObservers();

    }

}

重头戏:LiveDataBus来啦!

LiveData是一个可以被观察的数据持有类,他可以感知并遵循Activity,Fragmet或者Service等组件的生命周期。于此,可以做到仅在组件处于生命 周期激活状态时才更新UI数据。

LiveData需要一个观察者对象,一般是Observer类的具体实现,当观察者处于Stated或者Resumed状态时,LiveData会通知观察者数据变化。在观察者处于其他状态的=时,即使LiveData数据变化了,也不会通知。

优点:

LiveData的优点

UI和实时数据保持一致,因为LiveData采用的是观察者模式,这样一来就可以在数据发生改变时获得通知,更新UI。

避免内存泄漏,观察者被绑定到组件的生命周期上,当被绑定的组件销毁(destroy)时,观察者会立刻自动清理自身的数据。

不会再产生由于Activity处于stop状态而引起的崩溃,例如:当Activity处于后台状态时,是不会收到LiveData的任何事件的。

不需要再解决生命周期带来的问题,LiveData可以感知被绑定的组件的生命周期,只有在活跃状态才会通知数据变化。

实时数据刷新,当组件处于活跃状态或者从不活跃状态到活跃状态时总是能收到最新的数据。

解决Configuration Change问题,在屏幕发生旋转或者被回收再次启动,立刻就能收到最新的数据。

什么是Android Architecture Components ?

Android Architecture ComponentsAndroid Architecture Components的核心是Lifecycle、LiveData、ViewModel 以及 Room,通过它可以非常优雅的让数据与界面进行交互,并做一些持久化的操作,高度解耦,自动管理生命周期,而且不用担心内存泄漏的问题。

Android Architecture Components 特点 ?

数据驱动型编程变化的永远是数据,界面无需更改。感知生命周期,防止内存泄漏高度解耦数据,界面高度分离。数据持久化数据、ViewModel不与 UI的生命周期挂钩,不会因为界面的重建而销毁。

Room

一个强大的SQLite对象映射库。

ViewModel

一类对象,它用于为UI组件提供数据,在设备配置发生变更时依旧可以存活。

LiveData一个可感知生命周期、可被观察的数据容器,它可以存储数据,还会在数据发生改变时进行提醒。

Lifecycle

包含LifeCycleOwer和LifecycleObserver,分别是生命周期所有者和生命周期感知者。

重点:为什么使用LiveData构建数据通信总线LiveDataBus?

1,具有可观察性和生命周期感知能力

2,不用显示调用反注册

组成:

消息

消息可以是任何的Object,可以定义不同类型的消息,如Boolean、String。也可以定义自定义类型的消息。

消息通道

LiveData扮演了消息通道的角色,不同的消息通道用不同的名字区分,名字是String类型的,可以通过名字获取到一个LiveData消息通道。

消息总线

消息总线通过单例实现,不同的消息通道存放在一个HashMap中。

订阅

订阅者通过getChannel获取消息通道,然后调用observe订阅这个通道的消息。

发布

发布者通过getChannel获取消息通道,然后调用setValue或者postValue发布消息。

还在用RxBus&EventBus?你Out啦!哥哥教你用更简单的LiveData来代替Android事件总线_第2张图片
LiveDataBus原理图

第一个实现:

public finalclassLiveDataBus{

privatefinalMap> bus;

privateLiveDataBus(){

bus =newHashMap<>();

}

privatestaticclassSingletonHolder{

privatestaticfinalLiveDataBus DATA_BUS =newLiveDataBus();

}

publicstaticLiveDataBusget(){

returnSingletonHolder.DATA_BUS;

}

publicMutableLiveData getChannel(String target, Class type){

if(!bus.containsKey(target)) {

bus.put(target,newMutableLiveData<>());

}

return(MutableLiveData) bus.get(target);

}

publicMutableLiveDatagetChannel(String target){

returngetChannel(target, Object.class);

}

}

接下来是注册订阅:

LiveDataBus.get().getChannel("key_test", Boolean.class)

.observe(this,new Observer() {

@Override

publicvoidonChanged(@NullableBoolean aBoolean) {

}

});

接下来是发送消息:

LiveDataBus.get().getChannel("key_test").setValue(true);

但是这种实现会出现一个问题,那就是订阅者会收到订阅之前发布的消息。

所以最终的LiveDataBus封装如下:

public finalclassLiveDataBus{

privatefinalMap> bus;

privateLiveDataBus(){

bus =newHashMap<>();

}

privatestaticclassSingletonHolder{

privatestaticfinalLiveDataBus DEFAULT_BUS =newLiveDataBus();

}

publicstaticLiveDataBusget(){

returnSingletonHolder.DEFAULT_BUS;

}

publicMutableLiveData with(String key, Class type){

if(!bus.containsKey(key)) {

bus.put(key,newBusMutableLiveData<>());

}

return(MutableLiveData) bus.get(key);

}

publicMutableLiveDatawith(String key){

returnwith(key, Object.class);

}

privatestaticclassObserverWrapperimplementsObserver{

privateObserver observer;

publicObserverWrapper(Observer observer){

this.observer = observer;

}

@Override

publicvoidonChanged(@Nullable T t){

if(observer !=null) {

if(isCallOnObserve()) {

return;

}

observer.onChanged(t);

}

}

privatebooleanisCallOnObserve(){

StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();

if(stackTrace !=null&& stackTrace.length >0) {

for(StackTraceElement element : stackTrace) {

if("android.arch.lifecycle.LiveData".equals(element.getClassName()) &&

"observeForever".equals(element.getMethodName())) {

returntrue;

}

}

}

returnfalse;

}

}

privatestaticclassBusMutableLiveDataextendsMutableLiveData{

privateMap observerMap =newHashMap<>();

@Override

publicvoidobserve(@NonNull LifecycleOwner owner, @NonNull Observer observer){

super.observe(owner, observer);

try{

hook(observer);

}catch(Exception e) {

e.printStackTrace();

}

}

@Override

publicvoidobserveForever(@NonNull Observer observer){

if(!observerMap.containsKey(observer)) {

observerMap.put(observer,newObserverWrapper(observer));

}

super.observeForever(observerMap.get(observer));

}

@Override

publicvoidremoveObserver(@NonNull Observer observer){

Observer realObserver =null;

if(observerMap.containsKey(observer)) {

realObserver = observerMap.remove(observer);

}else{

realObserver = observer;

}

super.removeObserver(realObserver);

}

privatevoidhook(@NonNull Observer observer)throwsException{

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

        }

    }

}

注册订阅:

LiveDataBus.get()

        .with("key_test", String.class)

.observe(this,new Observer() {

@Override

publicvoidonChanged(@NullableString s) {

}

});

发送消息:

LiveDataBus.get().with("key_test").setValue(s);

源码库地址如下:

https://github.com/JeremyLiao/LiveDataBus

订阅者可以订阅某个消息通道的消息,发布者可以把消息发布到消息通道上。利用LiveDataBus,不仅可以实现消息总线功能,而且对于订阅者,他们不需要关心何时取消订阅,极大减少了因为忘记取消订阅造成的内存泄漏风险。

你可能感兴趣的:(还在用RxBus&EventBus?你Out啦!哥哥教你用更简单的LiveData来代替Android事件总线)