软件架构模式——事件总线模式
首先我来解释一下什么是事件总线模式。提到事件总线模式你可能很陌生,不知道是什么,那么我们换个说法,软件设计模式中有一种叫做观察者模式,其实事件总线模式就是对观察者模式的一种实现,它是一种集中式事件处理机制,允许不同的组件之间进行彼此通信而又不需要相互依赖,达到一种解耦的目的,他就是对观察者模式的一种拓展。如果你对观察者模式不太熟悉的话,建议自己找找资料理解一下。接下来我通过Android里面的机制来讲解一下事件总线模式。
在Android开发中,经常会涉及Activity,Fragment,Service等不同组件或者模块之间的消息传递。使用传统的方法实现,往往代码不够优雅,而且不同组件和模块之间的耦合严重。随着模块的增多、代码逻辑的不断新增和修改,整个代码的架构就会显得越来越混乱。Activity中的不同的fragment之间需要进行通信,传统的做法是 将activity作为中介,Fragment A通过getActivity()获取宿主的Activity实例进而可以拿到Fragment B的实例,从而向Fragment B发送消息或者获取数据。好一点的做法是在Fragment中编写接口,让宿主Activity实现该接口,从而在Activity中实现不同Fragment之间的数据通信。或者多个Activity页面跳转和数据回传的问题,例如Activity A跳转到Activity B,接着跳转到ActivityC,在C中执行一系列操作之后,需要传递数据或者事件给Activity A,传统的做法是进行接口回调,这样不仅增加逻辑复杂性,而且增大页面间的耦合。
发布者Publisher:发布某种事件的对象。
事件Event:一个简单的POJO对象。只包含数据,不对数据进行处理。
事件总线EventBus: 负责订阅者,事件等信息的存储,同时处理事件的流动和分发,通过总线,订阅者和发布者是解耦的,互相不知道对方的存在。
订阅者Subscriber: 订阅某种类型事件的对象。通常会有一个回调函数用于对接收到的事件进行处理,订阅者可以订阅事件,也可以去掉订阅的事件。订阅者可以引入优先级的概念,优先级高的订阅者可以优先接收到该事件,并可以决定是否继续传递事件给低优先级的订阅者。
EventBus一共提供了4种线程模型ThreadModel,分别是PostThread, MainThread, BackgroundThread, Async。
PostThread --------------默认实现,执行发生在发布事件的同一个线程;
MainThread --------------执行在UI主线程上;
BackgroundThread、Async---两个都是通过Executors.newCachedThreadPool()线程池来执行的。
Eventbus作为android开发中的常用框架,拥有着许多优点:
调度灵活:不依赖于Context,使用时无需像广播一样关注Context的注入与传递。父类对于通知的监听和处理可以继承给子类。这对于简化代码至关重要。通知的优先级,能够保证Subscriber关注最重要的通知;粘滞事件能够保证通知不会因Subscriber的不在场而忽略。 可继承,优先级,粘滞是eventBus比之于广播,观察者等方式最大的优点,他们使得创建结构良好组织紧密的通知系统成为可能。
使用简单:Eventbus的Subscriber注册非常简单,调用eventBus对象的register方法即可,如果不想创建eventBus还可以直接调用静态方法EventBus.getDefault()获取默认实例,Subscriber接收到通知之后的操作放在onEvent方法里就行了。成为Publisher的过程更简单,只需要调用合适的eventBus(自己创建或者是默认的)的post方法即可。
快速且轻量:观察者这种设计模式应该属于程序员的基本功,由于观察者的实现比较简单,因此性能上是三者中最好的,但是观察者难以控制通知的优先度,特别是一开始没有考虑优先度中途更高需求又加入优先度。另外观察者模式要求观察者在事件发生时在场才能收到通知,这就是的观察者有可能遗漏事件。客观来说,这并不能算观察者的缺点,因为其他的方式往往也是这样,更加严谨的说法是观察者没有Eventbus优先级、粘滞事件的优点。但有一个缺点是观察者独有的,那就是观察者可能会造成接口的膨胀,特别是当程序要求大量形式各异的通知,而程序员没有做出良好的抽象时,代码中会包含大量的接口。