Otto源码分析

  • android
  • square
  • Otto
  • event bus
  • 源码分析

构造函数

使用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

1
2
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是生产事件的方法。
比如下面这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
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的返回值传入。

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

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

1
2
3
4
5
6
7
/** 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, Set>。其中EventHandler包含了subscribe方法和订阅事件的对象的信息。

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

发送事件

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

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

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

取消订阅

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