一、otto简介
otto是支付公司square一个专注于android的开源项目,该项目是一个event bus模式的消息框架,是一个基于Guava的增强型事件总线。旨在将应用程序的不同部分分离,同时仍然允许它们进行高效通信。也就是用于程序各个模块之间的通信。
项目链接:https://github.com/square/otto/tree/master/otto/src/main/java/com/squareup/otto
二、otto与观察者模式
在这之前,我们需要先了解一下什么是event bus?
我们在写程序的时候,通常需要让应用程序的各个组件之间进行消息传递(通信),各个组件之间不得不发生联系。但是当项目变得越来越大越来越复杂的时候,使用各种方法进行模块间通信、模块与后台线程进行通信时,往往代码量很大,而且他们的通信也导致了各个模块之间是高度耦合的。
EventBus可以代替Android传统的Intent,Handler,Broadcast或接口函数,在Fragment,Activity,Service线程之间传递数据,执行方法。(https://baike.baidu.com/item/EventBus/20461274)
也就是说,EventBus能够让各组件间的通信变得更简单,能有效的分离事件发送方和接收方,降低组件之间的耦合度,能避免复杂和容易出错的依赖性以及生命周期问题,同时也使代码变得更简洁,提高可读性,性能也更好。
otto是基于Guava的增强型事件总线(event bus)。通过发布和订阅事件,实际是注解和反射技术而实现的观察者模式。
观察者模式是一个使用率非常高的模式,它最常用在GUI系统、订阅–发布系统。举个例子:
我们平时都会有自己感兴趣的东西,假设我们对某些电视剧很感兴趣,我们就可以向视频软件订阅这些电视剧的开播消息,当有这些电视剧快要开播了,视频软件就马上将这些消息通知到我们。在这个例子中,“我们”是观察者,“开播消息”是被观察者。被观察者一旦发生变动,就主动通知观察者。
在这个例子中,我们可以采用一些手段来管理这些开播消息,以减少组件之间的耦合。这就是观察者模式。观察者模式可以将被观察者和观察者解耦,使得它们之间的依赖性更小,甚至做到毫无依赖。比如说,观察者模式可以通过一些抽象类或者接口,来减少实体类之间的联系,以此做到解耦合。
所以otto利用观察者模式有效的减少了各组件之间的耦合度。
三、otto源码解析
otto中一共有九个.java文件:
(1)AnnotatedHandlerFinder 注解解析器:
这个类用来负责通过反射获取 通过 @Produce 注释的方法和通过 @Subscribe 注释的方法。
(2)Bus 总线核心类
(3) DeadEvent 没有接收者的事件:
包裹一个已经提交的但是由于没有订阅者而不能发出的事件。
(4)EventHandler 事件订阅者
(5)EventProducer 事件生产者
(6)HandlerFinder 获取接收者生产者:
本身是一个接口,用于发现生产者和订阅者。
(7)Produce 生产者的注解:
标记一个方法作为实例生产者,被AnnotatedHandlerFinder和Bus类使用
(8)Subscribe 订阅者注:
将方法标记作为一个事件处理方法,同Produce一样也是在AnnotatedHandlerFinder和Bus类中使用。
(9)ThreadEnforcer对线程进行校验:
本身是一个接口,其内部有一个接口方法。otto确保收到的回调总是在自己想要的线程上,默认一个实例的所有交互都是局限于主线程上的。
要分析源代码,我们从这个库是怎么使用的来入手。我们在使用的过程中,最常用也是直接使用的其实是Bus这个类。
Bus 总线核心类
Bus.java将事件调度给侦听器,并为侦听器提供注册自己的方法。总线允许组件之间的发布 - 订阅式通信,而不需要组件彼此明确地注册(因此彼此了解)。 它专门用于使用显式注册或侦听器替换传统的Android进程内事件分发。 它不是通用发布 - 订阅系统,也不是用于进程间通信。
(1)首先利用Bus()构造函数在整个app中创建一个单例,这样不但节省资源,还能保证消息正常到达。Bus的构造函数如下:
1 public class Bus { 2 public static final String DEFAULT_IDENTIFIER = "default"; 3 4 /** All registered event handlers, indexed by event type. */ 5 private final ConcurrentMap<Class<?>, Set<EventHandler>> handlersByType = 6 new ConcurrentHashMap<Class<?>, Set<EventHandler>>(); 7 8 /** All registered event producers, index by event type. */ 9 private final ConcurrentMap<Class<?>, EventProducer> producersByType = 10 new ConcurrentHashMap<Class<?>, EventProducer>(); 11 12 /** Identifier used to differentiate the event bus instance. */ 13 private final String identifier; 14 15 /** Thread enforcer for register, unregister, and posting events. */ 16 private final ThreadEnforcer enforcer; 17 18 /** Used to find handler methods in register and unregister. */ 19 private final HandlerFinder handlerFinder; 20 21 /** Queues of events for the current thread to dispatch. */ 22 private final ThreadLocal<ConcurrentLinkedQueue<EventWithHandler>> eventsToDispatch = 23 new ThreadLocal<ConcurrentLinkedQueue<EventWithHandler>>() { 24 @Override protected ConcurrentLinkedQueue<EventWithHandler> initialValue() { 25 return new ConcurrentLinkedQueue<EventWithHandler>(); 26 } 27 }; 28 29 /** True if the current thread is currently dispatching an event. */ 30 private final ThreadLocal<Boolean> isDispatching = new ThreadLocal<Boolean>() { 31 @Override protected Boolean initialValue() { 32 return false; 33 } 34 }; 35 36 /** Creates a new Bus named "default" that enforces actions on the main thread. */ 37 public Bus() { 38 this(DEFAULT_IDENTIFIER); 39 } 40 41 /** 42 * Creates a new Bus with the given {@code identifier} that enforces actions on the main thread. 43 * 44 * @param identifier a brief name for this bus, for debugging purposes. Should be a valid Java identifier. 45 */ 46 public Bus(String identifier) { 47 this(ThreadEnforcer.MAIN, identifier); 48 } 49 50 /** 51 * Creates a new Bus named "default" with the given {@code enforcer} for actions. 52 * 53 * @param enforcer Thread enforcer for register, unregister, and post actions. 54 */ 55 public Bus(ThreadEnforcer enforcer) { 56 this(enforcer, DEFAULT_IDENTIFIER); 57 } 58 59 /** 60 * Creates a new Bus with the given {@code enforcer} for actions and the given {@code identifier}. 61 * 62 * @param enforcer Thread enforcer for register, unregister, and post actions. 63 * @param identifier A brief name for this bus, for debugging purposes. Should be a valid Java identifier. 64 */ 65 public Bus(ThreadEnforcer enforcer, String identifier) { 66 this(enforcer, identifier, HandlerFinder.ANNOTATED); 67 } 68 69 /** 70 * Test constructor which allows replacing the default {@code HandlerFinder}. 71 * 72 * @param enforcer Thread enforcer for register, unregister, and post actions. 73 * @param identifier A brief name for this bus, for debugging purposes. Should be a valid Java identifier. 74 * @param handlerFinder Used to discover event handlers and producers when registering/unregistering an object. 75 */ 76 Bus(ThreadEnforcer enforcer, String identifier, HandlerFinder handlerFinder) { 77 this.enforcer = enforcer; 78 this.identifier = identifier; 79 this.handlerFinder = handlerFinder; 80 } 81 82 @Override public String toString() { 83 return "[Bus \"" + identifier + "\"]"; 84 }
在构造方法中,有两个参数:
其中,ThreadEnforcer -- 这个是确保bus运行在指定的线程
HandlerFinder -- 这个是用来提前事件生产者和操作者的注册和取消注册的
(2)构造之后,需要把监听事件的组件或者发送事件的组件注册进去==>调用Bus的register方法。在类创建好之后,或者需要重新注册的时候注册,一般在Activity的onCreate()或者onPause()方法中。
if (object == null) {
throw new NullPointerException("Object to register must not be null.");
}
enforcer.enforce(this);
Map<Class<?>, EventProducer> foundProducers = handlerFinder.findAllProducers(object);
for (Class<?> type : foundProducers.keySet()) {
final EventProducer producer = foundProducers.get(type);
EventProducer previousProducer = producersByType.putIfAbsent(type, producer);
//checking if the previous producer existed
if (previousProducer != null) {
throw new IllegalArgumentException("Producer method for type " + type
+ " found on type " + producer.target.getClass()
+ ", but already registered by type " + previousProducer.target.getClass() + ".");
}
Set<EventHandler> handlers = handlersByType.get(type);
if (handlers != null && !handlers.isEmpty()) {
for (EventHandler handler : handlers) {
dispatchProducerResultToHandler(handler, producer);
}
}
}
需要注册所有事件处理方法方法以接收事件,以及生产者方法以提供事件。
*如果任何订阅者正在注册已经拥有生产者的方法,则会立即调用该生产者。
*如果正在注册的生产者已经拥有订阅者,则将调用事件产生方法,把事件分发给事件订阅者。
从上面的代码可以看出:首先获取所有的@Producer注释的方法,然后获取所有的@Subscribe注释的函数,最后将两者关联起来,这样,在调用post()方法时,就能实现数据传递的效果了。
在注册对象之后,会解析出对象对应的类的生产方法和订阅方法,订阅者解析的结果保存在handlersByType,生产者解析的结果保存在producersByType里对象注册首先进行了非空校验,然后是线程的校验。
resister中使用put方法,enforcer.enforce(this)这句是用来确保运行的线程的。foundProducers是用来发现所有的生产者。foundHandlersMap这个用来获取所有的订阅者。
(3)定义订阅方法
这个方法自己实现,看具体功能。
(4)发送消息
发送消息需要调用Bus的post()方法。post()分发事件到所有注册这个事件的方法中去。如果事件分发失败,会封装一个DeadEvent对象,然后重新分发,但是这个DeadEvent对象没有被处理。
2 if (event == null) {
3 throw new NullPointerException("Event to post must not be null.");
4 }
5 enforcer.enforce(this);
6
7 Set<Class<?>> dispatchTypes = flattenHierarchy(event.getClass());
8
9 boolean dispatched = false;
10 for (Class<?> eventType : dispatchTypes) {
11 Set<EventHandler> wrappers = getHandlersForEventType(eventType);
12
13 if (wrappers != null && !wrappers.isEmpty()) {
14 dispatched = true;
15 for (EventHandler wrapper : wrappers) {
16 enqueueEvent(event, wrapper);
17 }
18 }
19 }
20
21 if (!dispatched && !(event instanceof DeadEvent)) {
22 post(new DeadEvent(this, event));
23 }
24
25 dispatchQueuedEvents();
26 }
其中,Set<Class<?>> dispatchTypes = flattenHierarchy(event.getClass())返回这个event的所有继承关系链的所有class对象(父类class对象和自己); Set<EventHandler> wrappers = getHandlersForEventType(eventType)这句是用来获得和eventType相关的注册方法。
总的来说:当调用 public void post(Object event)这个方法之后,首先进行线程校验,然后解析出对应的订阅者,如果有订阅者,将event放入队列中, 如果没有,就作为一个DeadEvent。
Bus在分发消息之后循环从消息队列中取值,这跟android的handler消息机制很像,不过bus中的循环在消息取完之后就结束了。消息队列使用ThreadLocal保证了队列的独立性。同时多个线程会创建多个循环,提高了效率。发送的消息很快就就可以分发。
(5)解绑(注销)
解绑也就是调用注销方法unregister(listener),删除和这个listener对象相关的生产事件的方法和注册监听的方法。
代码如下:
1 public void unregister(Object object) { 2 if (object == null) { 3 throw new NullPointerException("Object to unregister must not be null."); 4 } 5 enforcer.enforce(this); 6 7 Map<Class<?>, EventProducer> producersInListener = handlerFinder.findAllProducers(object); 8 for (Map.Entry<Class<?>, EventProducer> entry : producersInListener.entrySet()) { 9 final Class<?> key = entry.getKey(); 10 EventProducer producer = getProducerForEventType(key); 11 EventProducer value = entry.getValue(); 12 13 if (value == null || !value.equals(producer)) { 14 throw new IllegalArgumentException( 15 "Missing event producer for an annotated method. Is " + object.getClass() 16 + " registered?"); 17 } 18 producersByType.remove(key).invalidate(); 19 } 20 21 Map<Class<?>, Set<EventHandler>> handlersInListener = handlerFinder.findAllSubscribers(object); 22 for (Map.Entry<Class<?>, Set<EventHandler>> entry : handlersInListener.entrySet()) { 23 Set<EventHandler> currentHandlers = getHandlersForEventType(entry.getKey()); 24 Collection<EventHandler> eventMethodsInListener = entry.getValue(); 25 26 if (currentHandlers == null || !currentHandlers.containsAll(eventMethodsInListener)) { 27 throw new IllegalArgumentException( 28 "Missing event handler for an annotated method. Is " + object.getClass() 29 + " registered?"); 30 } 31 32 for (EventHandler handler : currentHandlers) { 33 if (eventMethodsInListener.contains(handler)) { 34 handler.invalidate(); 35 } 36 } 37 currentHandlers.removeAll(eventMethodsInListener); 38 } 39 }
跟register类似,首先是对象非空校验,然后是线程校验,然后解绑,注册跟解绑一定要成对,没有注册不可以解绑,解绑之后不可以直接再次解绑。
四、otto使用观察者模式的优缺点
otto多数实现依赖注解反射。android事件总线处理还有个很好的开源框架是EventBus,EventBus稍微重量级一些,复杂一些,对应的功能更多,对线程控制更加灵活。
可以看到观察者模式有以下优点:
(1)观察者模式解除了主题和具体观察者的耦合,让耦合的双方都依赖于抽象,而不是依赖具体。从而使得各自的变化都不会影响另一边的变化,这样就可以很好的应对业务变化。
(2)增强了系统的灵活性和可扩展性。
缺点:
在应用观察者模式时需要考虑一下开发效率和运行效率的问题,程序中包括一个被观察者和多个观察者,开发调试等内容会比较复杂,而且在Java中消息的通知默认是顺序执行的,一个观察者卡顿,会影响整体的执行效率,在这种情况下一般考虑采用异步的方式。
参考博客:https://blog.csdn.net/robertcpp/article/details/51628749?utm_source=blogxgwz11