与EventBus类似的还有otto,于是也学习了一下。在网上找到的大部分博客其实都是对otto官方文档的中文翻译,对生产者的@Produce、PUBLISHING、PRODUCING之间的关系讲得比较模糊,也没有对EventBus的StickyEvnet与Otto中类似机制的对比,而这正是我想了解的。于是就自己写demo实验,以此文总结。
本文的Otto指的是:square/otto
EventBus指的是:greenrobot/EventBus
Github上有很多同名项目,不要搞错。
本文档前面是对Otto各方面的介绍。如果要学习快速使用,可以直接跳转到后面的详细使用实例部分。
特点
Otto的作用与EventBus类似,都是用于组件间通信,降低不同的类相互之间的耦合。如果不熟悉EventBus,可以先了解EventBus,下文涉及到otto与EventBus的对比。
Otto可以随便定义订阅者的消息处理函数的函数名,采用注解的方式来识别生产函数和消费函数(@Produce和@Subscribe),这是与EventBus明显的不同。
PUBLISHING、SUBSCRIBING与PRODUCING
虽然同样是订阅者模式的实现,但是Otto与Eventbus相比,多了一个“PRODUCING”的概念(Eventbus中只有PUBLISHING和SUBSCRIBING的概念)。
其实这里的PRODUCING设计的初衷与Eventbus中的StickyEvent是相似的,都是为了能够取到某中类型的事件的最新值,但在实现上有较大差异。
订阅事件
用来消耗(或者说处理)事件的是订阅者,必须用@Subscribe标注。 订阅者(假定是一个Activity)需要在初始化时(onCreate()方法或onResume()方法中)使用register()方法向总线注册,在onDestroy()或onPause()时调用unregister()从总线反注册。
发布事件
PUBLISHING
而产生(或者说发布)事件的方式有两种,一种是不使用@Produce标注,这种方式被otto官方文档称为PUBLISHING,方法是直接调用bus对象的post方法,如下:1
MyApplicationUtil.bus.post(new TextEvent("这是PUBLISHING事件: " + new Date()));
PRODUCING
另一种是使用@Produce标注,这种方式被otto官方文档称为PRODUCING。如果使用了@Produce来标注生产者(即实现PRODUCING功能),就需要同时在初始化时使用bus.register(this)来注册这个生产者,在销毁组件时调用unregister()。
PRODUCING其实类似于EventBus中的StickyEvent。 用rigister方法注册@Produce标注的生产者(被注册的函数需要返回一个事件实例)时, @Produce标注的生产者函数会为所有之前注册了该事件类型的订阅者分别进行一次回调,并且此生产者函数也将为在此之后注册的该事件类型的订阅者分别进行一次回调。
Otto没有EventBus中那种可以在任意地方获取StickyEvent的方法,因为虽然PRODUCING与StickyEvent类似,但Eventbus是通过一个map来缓存StickyEvent的最后一次更新值,不会触发任何函数回调,而Otto的PRODUCING并没有这种缓存,是实时去回调Produce函数来拿到最新值的。
PRODUCING的定义如下面代码所示:
1 |
|
这里仅仅是为了实验调试是不是每个订阅者都触发这个回调,所以才写成每次return的都是new出来的新对象。事实上,Otto的文档中说@Produce机制是为了便于多个订阅者能全都某个事件的最新状态,比如最后一次定位的位置。所以,要实现官方文档中说的这一点,就不能在@Produce函数中每次return新对象,而是return一个外部的全局对象(event),这个对象在外围被其他逻辑更新,而不是不在@Produce函数中被更新。
在一个总线上(一个bus对象)上,同一时刻同一类型的用@Produce标注的事件生产者只能有一个。官方文档的原文是:
You may only have one producer per event type registered at a time on a bus.
一般在使用EventBus时,会直接采用EventBus.getDefault()来获取系统默认提供的单例对象,然后再该对象(总线对象)上进行实践的发布和订阅。但是otto没有提供默认的单例,一般需要自己在应用程序范围内,自己去手动创建并维护一个otto的bus对象。
Otto在初始化Bus的时候来决定处理事件的执行线程。默认只有ThreadEnforcer.ANY和ThreadEnforcer.MAIN两种。如果需要其他类型的线程控制,需要自己实现ThreadEnforcer接口。相对而言,EventBus的控制更精细更简单。
与EventBus的对比
详细使用实例
1.在build.gradle的dependencies中加入以下内容:
1 | compile 'com.squareup:otto:1.3.8' |
2.根据业务需要定义事件
1 | public class TextEvent { |
3.创建一个类,在整个应用程序范围内持有一个总线对象。(使用EventBus不需要这一步,因为EventBus已近替我们实现好了默认的单例对象)
1 | public class MyApplicationUtil { |
4.在需要订阅事件的组件中(即订阅者,假设是一个Activity),做以下三个工作:
- 在初始化组件时向总线注册自己为某类型事件的订阅者
1 |
|
- 实现事件处理函数
1 |
|
- 在组件销毁时反注册订阅者
1 |
|
5.在产生事件的组件(生产者)中,如果只是发送普通事件,就直接调用bus对象的post方法即可,之前注册的订阅者就可以接收到这个事件了。
1 | MyApplicationUtil.bus.post(new TextEvent(dataEditText.getText().toString())); |
6.如果需要实现PRODUCING功能(即类似StickyEvent功能),就需要用@Producer显示标注生产者,同时也要对该生产者进行注册和反注册,如下面代码所示。
1 |
|
按照这种方式显示定义了生产者之后,之前在此bus上注册过的所有该类型事件的订阅者都会接收到本事件的实例。之后再再此bus上注册的此类型的新的订阅者同样能够接收到本事件实例。
参考资料