Android消息总线LiveDataBus

Android消息总线LiveDataBus

Android 的生命周期比较复杂,一般情况下只能覆写 Activity / Fragment 的回调方法(onCreate、onResume、onPause、onStop、onDestroy 等)才能监听生命周期,样板代码少不了,可维护性也较差。

Google 为了帮助 Android 开发者更快更好地开发 App,推出了一系列组件,这些组件被打包成了一个整体,称作 Android Jetpack,其中部分组件组成了Android Architecture Components,以下简称 AAC。

其中LiveData是其中一个组件, LiveData是一个观察者模型,观察 Lifecycle 的变化从而影响数据的分发。正是由于LiveData对组件生命周期可感知特点,因此可以做到仅在组件处于生命周期的激活状态时才更新UI数据。

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

LiveDataBus 与其他总线对比

优点

  • LiveData具有生命周期感知的能力,只有在活跃状态才会通知数据变化,非常适合作为Android通信总线的基础构件。
  • 与其他总线相比,LiveData使用者不用显示调用反注册方法,避免内存泄露等其他界面结束后的异常情况。
  • LiveDataBus 实现简单,依赖官方组件LiveData。如果已经使用了AAC或者 Android Jetpack全家桶LiveData几乎不会增加包大小。

缺点

  • LiveData 只存储最新的数据,setValue在UI线程执行,及时更新状态到活跃页面,postValue非UI线程更新状态,快速调用只有最新的数据会被送达。虽然LiveData用法类似 RxJava2 的 Flowable,但是它不支持背压(backpressure),所以不是一个流(stream),利用 LiveDataReactiveStreams 我们可以实现 Flowable 和 LiveData 的互换。
  • LiveData 是一个观察者模型,但是它是一个与 Lifecycle 绑定了的 Subject,也就是说,只有当 UI 组件处于 ACTIVE 状态时,它的 Observer 才能收到消息,否则会自动切断订阅关系,不会像 RxJava 那样可以通过 CompositeDisposable 来手动处理。这是个优点,同时在某些场景也会被认为是缺点。
  • LiveData 的数据天生是sticky的,在LiveData后注册的observe会收到上次LiveData发送的消息,如果我们要把 LiveData 用作事件总线,还需要做一些定制。

LiveDataBus 的实现

public class LiveDataBus {

    private static volatile LiveDataBus instance;

    private final ConcurrentHashMap<Object, BusLiveData<Object>> mBus;

    private LiveDataBus() {
        mBus = new ConcurrentHashMap<>();
    }

    public static LiveDataBus get() {
        if (instance == null) {
            synchronized (LiveDataBus.class) {
                if (instance == null) {
                    instance = new LiveDataBus();
                }
            }
        }
        return instance;
    }

    public <T> MutableLiveData<T> subscribe(Object eventKey, Class<T> type) {
        String key = eventKey.toString();
        if (mBus.containsKey(key) && mBus.get(key) != null) {
            BusLiveData busLiveData = mBus.get(key);
            busLiveData.firstSubscribe = false;
        } else {
            mBus.put(key, new BusLiveData<>(key, true));
        }

        return (MutableLiveData<T>) mBus.get(key);
    }

    public <T> MutableLiveData<T> setValue(T eventKey, T value) {
        Class<T> tClass = (Class<T>) value.getClass();
        MutableLiveData<T> mutableLiveData = subscribe(eventKey, tClass);
        mutableLiveData.setValue(value);
        return mutableLiveData;
    }

    public <T> MutableLiveData<T> postValue(T eventKey, T value) {
        Class<T> tClass = (Class<T>) value.getClass();
        MutableLiveData<T> mutableLiveData = subscribe(eventKey, tClass);
        mutableLiveData.postValue(value);
        return mutableLiveData;
    }

    public static class BusLiveData<T> extends MutableLiveData<T> {

        private boolean firstSubscribe;
        private String key;

        BusLiveData(String key, boolean firstSubscribe) {
            this.key = key;
            this.firstSubscribe = firstSubscribe;
        }

        @Override
        public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
            super.observe(owner, new ObserverWrapper<>(observer, firstSubscribe));
        }

        @Override
        public void observeForever(@NonNull Observer<? super T> observer) {
            super.observeForever(new ObserverWrapper<>(observer, firstSubscribe));
        }

        @Override
        public void removeObserver(@NonNull Observer<? super T> observer) {
            super.removeObserver(observer);
            if (!hasObservers()) {
                LiveDataBus.get().mBus.remove(key);
            }
        }
    }

    private static class ObserverWrapper<T> implements Observer<T> {

        private Observer<T> observer;

        private boolean isChanged;

        private ObserverWrapper(Observer<T> observer, boolean isFirstSubscribe) {
            this.observer = observer;
            isChanged = isFirstSubscribe;
        }

        @Override
        public void onChanged(@Nullable T t) {
            if (isChanged) {
                if (observer != null) {
                    observer.onChanged(t);
                }
            } else {
                isChanged = true;
            }
        }
    }
}

上面提到LiveData的缺点时说过:在LiveData后注册的observe会收到上次LiveData发送的消息,所以上面代码定制了firstSubscribe字段避免这种情况发生。
使用方法:

        LiveDataBus.get().subscribe("test", Boolean.class).observe(this, new Observer<Boolean>() {
            @Override
            public void onChanged(@Nullable Boolean aBoolean) {

            }
        });

ps:如果希望在任何时候都监听消息可以使用observeForever方法,如果只是希望在更广阔的生命周期监听,比如说在create到destroy之间监听事件,可以参考继承修改LifecycleBoundObserver类的shouldBeActive方法,具体实现参考ExternalLiveData

作者codyer,源码ElegantBus,ElegantBus 是一款 Android 平台,基于 LivaData消息总线框架,这是一款非常 优雅 的消息总线框架。如果对 ElegantBus 的实现过程,以及考虑点感兴趣的可以看看前几节自吹如果只是想先使用的,可以跳过,直接到跳到使用说明和常见 LivaData 实现的 EventBus 比较消息总线使用反射入侵系统包名进程内 Sticky跨进程 Sticky跨 APP Sticky事件可配置化线程分发消息分组跨 App 安全考虑常驻事件 StickyLiveEventBus:white_check_mark::white_check_mark::white_check_mark::x::x::x::x::x::x::x:ElegantBus:x::x::white_check_mark::white_check_mark::white_check_mark::white_check_mark::white_check_mark::white_check_mark::white_check_mark::white_check_mark:来龙去脉自吹ElegantBus 支持跨进程,且支持跨应用的多进程,甚至是支持跨进程间的粘性事件,支持事件管理,支持事件分组,支持自定义事件,支持同名事件等。之所以称之为最优雅的总线,是因为她不仅实现了该有的功能,而且尽量选用最合适,最轻量,最安全的方式去实现所有的细节。 更值得夸赞的是使用方式的优雅!前言随着 LifeCycle 的越来越成熟,基于 LifeCycle 的 LiveData 也随之兴起,业内基于 LiveData 实现的 EventBus 也如雨后春笋一般拔地而起。出于对技术的追求,看过了无数大牛们的实现,各位大神们思路也是出奇的神通,最基础的 LiveData 版 EventBus 其实大同小异,一个单例类管理所有的事件 LivaData 集合。如果不清楚的可以随便网上找找反正基本功能 LivaData 都支持了,实现 EventBus 只需要把所有事件管理起来就完事了。业内基于 LiveData 实现的 EventBus,其实考虑的无非就是下面提到的五个挑战,有的人考虑的少,有的人考虑的多,于是各种方案都有。ElegantBus 主要是集合各家之优势,进行全方面的考虑而产生的。五个挑战 之 路途险阻挑战一 : 粘性事件背景 LivaData 的设计之初是为了数据的获取,因此无论是观察开始之前产生的数据,还是观察开始之后产生的数据,都是用户需要的数据,只要是有数据,当 LifeCycle 处于激活状态,数据就会传递给观察者。这个我们称之为 粘性数据。 这种设计对于事件来说有时候就不那么友好了,之前的事件用户可能并不关心,只希望收到注册之后发生的事件。挑战二 : 多线程发送事件可能丢失背景 同样是因为使用场景的原因,LivaData 设计在跨线程时,使用 post 提交数据,只会保留最后一次数据提交的值,因为作为数据来说,用户只需要关心现在有的数据是什么。挑战三 : 跨进程事件总线背景 有时候我们应用需要设置多进程,不同模块可能允许在不同进程中,因为单例模式每个进程都有一份实体,所有无法达到跨进程,这时候设计 IP 方案选择。说明 这里提一下为什么不选用广播方式,对广播有一定了解的都知道,全局广播会有信息泄露,信息干扰等问题,而且开销也比较大,因此全局广播并不适合这种情况。 也许有人会说可以用本地广播,然而,本地广播目前来说并不是很好的选择。Google 官方也在 LocalBroadcastManager 的说明里面建议使用 LiveData 替代: 原文地址原文如下:2018 年 12 月 17 日版本 1.1.0-alpha01 中将弃用 androidx.localbroadcastmanager。原因LocalBroadcastManager 是应用级事件总线,在您的应用中使用了层违规行为;任何组件都可以监听来自其他任何组件的事件。 它继承了系统 BroadcastManager 不必要的用例限制;开发者必须使用 Intent,即使对象只存在且始终存在于一个进程中。由于同一原因,它未遵循功能级 BroadcastManager。 这些问题同时出现,会对开发者造成困扰。替换您可以将 LocalBroadcastManager 替换为可观察模式的其他实现。合适的选项可能是 LiveData 或被动流,具体取决于您的用例。更明显的原因是,本地广播好像并不支持跨进程~挑战四 : 跨应用(权限问题以及粘性问题)背景 跨进程相对来说还比较好实现,但是有的时候用户会有跨应用的需求,其实这个也是 IPC 范畴,为什么单独提出
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值