为何要使用
android应用开发中,经常会涉及Activity,Fragment,Service等不同组件或者模块之间的消息传递。使用传统的方法实现,往往代码不够优雅,而且不同组件和模块之间的耦合严重。随着模块的增多、代码逻辑的不断新增和修改,整个代码的架构就会显得越来越混乱。为了便于理解,下面举例说明:
例1
Activity中的不同的fragment之间需要进行通信,传统的做法是 将activity作为中介,Fragment A通过getActivity()获取宿主的Activity实例进而可以拿到Fragment B的实例,从而向Fragment B发送消息或者获取数据。好一点的做法是在Fragment中编写接口,让宿主Activity实现该接口,从而在Activity中实现不同Fragment之间的数据通信。
如链接:https://blog.csdn.net/yangyin3096/article/details/51476453
例2
多个Activity页面跳转和数据回传的问题,例如Activity A跳转到Activity B,接着跳转到ActivityC,在C中执行一系列操作之后,需要传递数据或者事件给Activity A,传统的做法是进行接口回调,这样不仅增加逻辑复杂性,而且增大页面间的耦合。
为了解决上面的问题,实现组件间和模块间的解耦,我们引入事件总线的概念。
原理
事件总线,是消息或者说事件流动的管道,不同组件和模块之间的消息传递都是通过事件总线来实现,组件与组件,模块与模块之间不直接进行通信。
事件总线是基于观察者模式思想实现的,它使用发布订阅的方式支持组件和模块间的通信,摒弃了观察者模式需要显示注册回调的缺点,同时可用于替换java中传统的事件监听方式。
事件Event
一个普通的POJO类,只包含数据,不包含对数据的操作。事件有两种类型:普通事件和粘滞事件。粘滞事件的特点是事件发布后,订阅者才开始订阅该类型事件,那么它依旧可以收到这个事件,而普通事件是收不到的。订阅者Subscriber
订阅某种类型事件的对象。通常会有一个回调函数用于对接收到的事件进行处理,订阅者可以订阅事件,也可以去掉订阅的事件。订阅者可以引入优先级的概念,优先级高的订阅者可以优先接收到该事件,并可以决定是否继续传递事件给低优先级的订阅者。发布者
事件的源头,发布某种类型事件的对象总线EventBus
负责订阅者,事件等信息的存储,同时处理事件的流动和分发,通过总线,订阅者和发布者是解耦的,互相不知道对方的存在。
EventBus四种ThreadMode
EventBus 3.0有以下四种ThreadMode:
![](/qrcode.jpg)
POSTING(默认):如果使用事件处理函数指定了线程模型为POSTING,那么该事件在哪个线程发布出来的,事件处理函数就会在这个线程中运行,也就是说发布事件和接收事件在同一个线程。在线程模型为POSTING的事件处理函数中尽量避免执行耗时操作,因为它会阻塞事件的传递,甚至可能会引起ANR。
MAIN:事件的处理会在UI线程中执行,事件处理时间不能太长,长了会ANR。
BACKGROUND:如果事件是在UI线程中发布出来的,那么该事件处理函数就会在新的新的线程中运行,如果事件本来就是子线程中发布出来的,那么该事件的处理函数直接在发布时间的线程中执行。在此事件处理函数中禁止对UI进行更新操作。
ASYNC:无论事件在哪个线程中发布,该事件处理函数都会在新建的子线程中执行,同样,此事件处理函数中禁止进行UI更新操作。
观察者、Eventbus和本地广播的使用选择
一般认为本地广播是三种方式中消耗时间空间最多的一种方式,但也是android相性最好的方式。因为广播属于android四大组件之一,在BroadcastReceiver中的onReceive方法中可以获得Context、Intent参数。持有这两个参数便可以调用许多android sdk中的方法,这一优势另外两种方式很难弥补。无论是EventBus还是观察者,需要获得Context的话,往往都需要进行复杂的参数传递或者依赖注入。
本地广播的另一个优点是,许多系统级的事件都是使用广播来进行通知的,像常用的电量变化、网络状态变化、短信发送接受的状态等等。这就使得与android系统相关的通知,广播往往成了唯一的选择。
但这并不意味着android系统中的通知都应该使用广播,因为相对于其他的方式而言,广播是重量级的、消耗资源较多的方式。广播的优势体现在它与android sdk链接的更紧密,当我们需要同android交互的时候,广播提供的便捷性抵消了它过多的资源消耗。但是对于不需要同android交互或者只是做很少的交互的时候,使用广播往往是一种浪费。
Eventbus作为android开发中的常用框架,拥有着许多优点:
- 调度灵活
不依赖于Context,使用时无需像广播一样关注Context的注入与传递。父类对于通知的监听和处理可以继承给子类。这对于简化代码至关重要。通知的优先级,能够保证Subscriber关注最重要的通知;粘滞事件能够保证通知不会因Subscriber的不在场而忽略。
可继承,优先级,粘滞是eventBus比之于广播,观察者等方式最大的优点,他们使得创建结构良好组织紧密的通知系统成为可能。
- 使用简单
Eventbus的Subscriber注册非常简单,调用eventBus对象的register方法即可,如果不想创建eventBus还可以直接调用静态方法EventBus.getDefault()获取默认实例,Subscriber接收到通知之后的操作放在onEvent方法里就行了。成为Publisher的过程更简单,只需要调用合适的eventBus(自己创建或者是默认的)的post方法即可。
- 快速且轻量
作为github的明星项目之一,EventBus的源代码中许多技巧来改善性能,
观察者这种设计模式应该属于程序员的基本功,由于观察者的实现比较简单,因此性能上是三者中最好的,但是观察者难以控制通知的优先度,特别是一开始没有考虑优先度中途更高需求又加入优先度。另外观察者模式要求观察者在事件发生时在场才能收到通知,这就是的观察者有可能遗漏事件。客观来说,这并不能算观察者的缺点,因为其他的方式往往也是这样,更加严谨的说法是观察者没有Eventbus优先级、粘滞事件的优点。
但有一个缺点是观察者独有的,那就是观察者可能会造成接口的膨胀,特别是当程序要求大量形式各异的通知,而程序员没有做出良好的抽象时,代码中会包含大量的接口。
参考:https://www.zhihu.com/question/33030182/answer/61063323
总结
事件总线使用起来很方便,但是我们不应该滥用它,需要严格限定它的使用范围。只有在组件或者模块间通信的时候才使用它,对于简单的消息传递,就选用观察者模式或者事件回调的方式即可。