Otto入门与源码分析

本文由csdn博主“大头鬼Bruce”的两篇博客:Otto使用入门Otto源码分析   组合而成。

Otto使用入门        

介绍

Otto 是square公司出的一个事件库(pub/sub模式),用来简化应用程序组件之间的通讯。

Otto 修改自Google的Guava库,专门为Android平台进行了优化。

使用

Otto本身是为Android平台专门开发的,使用的时候最好是使用单例模式。

Bus bus = new Bus();

bus对象只有作为单例共享的时候才足够高效,推荐使用依赖注入框架来注入单例对象或者采用类似的机制。

发布事件

发布一个事件很简单,调用post方法就可以,post方法可以接受任何类型

bus.post(new AnswerAvailableEvent(42));

订阅事件

订阅只需要在方法上加上@Subscribe注解,同时在适当的地方调用register

@Subscribe public void answerAvailable(AnswerAvailableEvent event) {
    // TODO: React to the event somehow!
}

注意subscribe方法接收的参数类型需要和post参数的类型一致或者是post参数类型的父类。

bus.register(this);

一旦调用了register方法,Otto就会通过反射去寻找所有带有@Subscribe或者@Produce注解的方法,并将这些方法缓存下来。只有 在调用了register之后,该类里面标注了@Subscribe或者@Produce的方法才会在适当的时候被调用。另外,当不需要订阅事件的时候, 可以调用unregister来取消订阅。

生产者

有时候当订阅某个事件的时候,希望能够获取当前的一个值,比如订阅位置变化事件的时候,希望能拿到当前的位置信息。Otto中@Produce正是扮演了这么一个生产者的角色。
@Produce也是用于方法,并且这个方法的参数必须为空,返回值是你要订阅的事件的类型。

@Produce public AnswerAvailableEvent produceAnswer() {
    // Assuming 'lastAnswer' exists.
    return new AnswerAvailableEvent(this.lastAnswer);
}

使用@Produce之后,也需要调用bus.register()。调用了register方法之后,所有之前订阅 AnswerAvailableEvent事件的方法都会被执行一次,参数就是produceAnswer方法的返回值,之后任何新的订阅了 AnswerAvailableEvent事件的方法,也都会立即调用produceAnswer方法。

线程限制

可以指定@Subscribe和@Produce标注的回调方法所运行的线程,默认是在MainThread中执行。

// 这两个方法是等价的
Bus bus1 = new Bus();
Bus bus2 = new Bus(ThreadEnforcer.MAIN);

如果不关心在哪个线程执行,可以使用ThreadEnforcer.ANY,甚至可以使用自己实现的ThreadEnforcer接口。

Otto源码分析

构造函数

使用Otto通常是通过一个Provider提供一个Bus单例。
首先我们来分析一下Bus的构造函数,Bus类的构造函数最终都会调用Bus(ThreadEnforcer enforcer, String identifier, HandlerFinder handlerFinder)这个构造函数。
其中enforcer用来限制执行register,unregister以及post event的线程,如果执行这些函数的线程不是enforcer指定的线程,就会抛出异常。
identifier相当于给Bus起的一个名字,在toString方法中使用。
handlerFinder是整个event bus的核心,用于在register,unregister的时候寻找所有的subscriber和producer。handlerFinder不需 要用户指定,默认使用HandlerFinder接口中定义的常量ANNOTATED,ANNOTATED本身就是HandlerFinder的匿名实 现。

注册

如果一个类对某些事件感兴趣,需要调用register方法来注册监听这些事件,监听通过在方法上使用@Subscribe来实现,Otto通过方法的参数来决定是否调用该方法。
register方法首先会调用handlerFinder的findAllProducers(object)方法去找到所有使用了@Produce注 解的方法。findAllProducers其实是委托AnnotatedHandlerFinder.findAllProducers方法。在 AnnotatedHandlerFinder中,定义了一个静态变量SUBSCRIBERS_CACHE

private static final Map<Class<?>, Map<Class<?>, Method>> PRODUCERS_CACHE =
      new HashMap<Class<?>, Map<Class<?>, Method>>();

PRODUCERS_CACHE 的key是监听类,就是调用bus.register()的类,value本身又是一个map,这个map的key是事件的class,value是生产事件的方法。
比如下面这个例子:

public class MainActivity extends Activity {
  @Inject Bus bus;
  @Override
  public void onResume() {
    bus.register(this);
  }
  @Produce public ClickEvent produceClick() {
      // TODO: React to the event somehow!
      return new ClickEvent();
  }
}

在PRODUCERS_CACHE中就会有一条记录,它的key是MainActivity.class,value对应的map中,key是ClickEvent.class,value是produceClick Method对象。
SUBSCRIBERS_CACHE:
MainActivity.class -》
ClickEvent.class -》produceClick

当调用AnnotatedHandlerFinder的findAllProducers方法时,会先根据传入的对象的类型,检查是否已经被缓存到 PRODUCERS_CACHE,如果没有的话,就会调用loadAnnotatedMethods,利用反射去寻找所有使用了@Produce注解的方 法,并且将结果缓存到PRODUCERS_CACHE中。最后,会从PRODUCERS_CACHE中取出监听类的所有Produce方法,遍历这些方 法,为一个方法构建一个EventProducer对象,并将这个EventProducer对象放到一个以事件的class作为key的map中,然后 返回这个map。EventProducer类包含了Produce方法和该方法所属的对象,并且提供了调用Produce方法的功能。

回到Bus的Register方法,调用完findAllProducers方法之后,会遍历传入的监听类的Produce方法,并且根据Produce 方法的返回值类型,来检查是否已经有对应的Subscribe存在,如果有的话,就会调用Subscribe方法,并将Producer的返回值传入。

Set<EventHandler> handlers = handlersByType.get(type);
if (handlers != null && !handlers.isEmpty()) {
  for (EventHandler handler : handlers) {
    dispatchProducerResultToHandler(handler, producer);
  }
}

这里需要注意的是,Bus对象两个map类型的常量,用来缓存所有事件的Producer和Subscriber。

/** All registered event handlers, indexed by event type. */
private final ConcurrentMap<Class<?>, Set<EventHandler>> handlersByType =
        new ConcurrentHashMap<Class<?>, Set<EventHandler>>();
/** All registered event producers, index by event type. */
private final ConcurrentMap<Class<?>, EventProducer> producersByType =
        new ConcurrentHashMap<Class<?>, EventProducer>();

从定义我们可以看出,一种事件只能有一个Producer,却可以有多个Subscriber。

找到了所有的producers之后,就是调用handlerFinder.findAllSubscribers(object)来寻找object中 使用@Subscribe注解的方法,过程和findAllProducers类似,唯一的不同是一个事件可以有多个subscriber,因此 findAllSubscribers的返回值类型是Map<Class<?>, Set>。其中EventHandler包含了subscribe方法和订阅事件的对象的信息。

找到监听类所有的subscribe方法之后,就需要查看bus中时候有和这些subscribe方法对应的producer方法,如果有的话,就会使用 调用subscribe方法。这也就是文档上说的,一旦有新的subscriber订阅了某一事件,并且该事件有对应的producer,那么 subscriber方法就会被立即调用,并且传入producer方法的返回值。

发送事件

post(Obejct event)方法用来发送事件给所有订阅者,它接收一个Object类型的参数,说明Otto的事件可以是任意类型的对象。post方法首先会获取所有event对象的父类

Set<Class<?>> dispatchTypes = flattenHierarchy(event.getClass());

然后遍历这些父类,找到他们的所有订阅者,发送事件。这表明任何订阅了event对象父类的订阅者也都会收到event事件。值得注意的是Otto使用 ThreadLocal类型来存放事件队列 ThreadLocal<ConcurrentLinkedQueue> eventsToDispatch,这样极大的简化了多线程模式下的开发。

取消订阅

unregister方法,做的事情和register刚好想法,从缓存中清除所有和当前监听对象相关的producers和subscribers。