注: 本文的EventBus版本为 3.0
Subscrib 注解
自 3.0 以来,EventBus 使用 @Subscrib 注解来标记订阅事件的方法,方法命名随意。并不用像以前那样指定方法的命名:
1 |
|
这个注解的定义很有个性,可以看下:
1 |
|
其中有几种线程模式,有以下几种:
ThreadMode.MAIN:如在主线程(UI线程)发送事件,则直接在主线程处理事件;如果在子线程发送事件,则先将事件入队列,然后通过 Handler 切换到主线程,依次处理事件。
ThreadMode.ASYNC:与ThreadMode.MAIN_ORDERED相反,无论在哪个线程发送事件,都将事件加入到队列中,然后通过线程池执行事件
ThreadMode.POSTING:默认的线程模式,在哪个线程发送事件就在对应线程处理事件,避免了线程切换,效率高。
ThreadMode.MAIN_ORDERED:无论在哪个线程发送事件,都将事件加入到队列中,然后通过Handler切换到主线程,依次处理事件。
ThreadMode.BACKGROUND:与ThreadMode.MAIN相反,如果在子线程发送事件,则直接在子线程处理事件;如果在主线程上发送事件,则先将事件入队列,然后通过线程池处理事件。
注册
注册过程很简单:
1 | EventBus.getDefault().register(this); |
根据源码可以直到,这个方法就做了两件事情:
- 根据传入的参数object ,获取其 Class ,然后通过这个 Class 获取所有的方法(当然,首先看有没有缓存),查看经过 @Subscrib 修饰的方法,即这个类中所有的订阅方法(会做封装),生成一个list
- 遍历上述生成的 list ,给 2 个 Map 填充数据:subscriptionsByEventType以 event (订阅方法的参数)的类型(Class)为key,value 为订阅方法list(CopyOnWriteArrayList);typesBySubscriber 以register时传入的对象为key ,value 为 这个对象所有订阅方法所订阅的事件。
1 | //EventBus中变量的声明 |
反注册
反注册也很简单:
1 | EventBus.getDefault().unregister(this); |
刚才在注册时候,说了其中一个 map 为 typesBySubscriber,它以注册对象为 key ,value 为这个对象中所有注册方法所注册的event 类型的列表。所以在反注册的时候,
- 首先通过传入的对象,获取 注册的 event 的列表
- 遍历这个列表,获取 event 的类型,然后通过这个类型在 subscriptionsByEventType 查找(经过封装的)订阅方法,根据封装在里面的 注册对象 是否是当前 unregister 传入的对象来判断,如果是当前传入的对象,就移除这个经过封装的订阅方法
post 发布事件
post的使用也很简单:
1 | EventBus.getDefault().post(new Object()); |
注册的时候,说了有一个map 为 subscriptionsByEventType ,以 event 的类型为key ,存储了所有订阅了这中 event 的(经过封装的)方法。post 的时候,根据post发送的事件类型(post方法的参数的 Class )从 subscriptionsByEventType 这个集合中获取到所有的订阅方法。之后依次通过反射调用这些方法:
1 | void invokeSubscriber(Subscription subscription, Object event) { |
其中, subscription.subscriberMethod.method 是 Method 类型的,用过反射的话,就知道它可以直接 invoke ,它的定义是这样的:
1 | public native Object invoke(Object obj, Object... args) |
第一个参数 obj 指的是用 obj 这个实例来调用这个方法(因为一个类可能会有多个实例,非静态方法需要指定一个实例来调用这个方法),后面的 args 就是方法需要传入的参数。
所以,在 invoke 的时候,subscription.subscriber 我们应该很容易知道是 register 时传入的那个 Object !由此,我们一个消息就形成了闭环。
最后问题,支持跨进程吗?
不支持跨进程,因为单例。经过上述分析,我们知道 EventBus 的注册、反注册、post 都是通过:EventBus.getDefault() 实现,我们看下它的代码:
1 | public class EventBus { |
所以我们都是基于一个 static 类型的 defaultInstance去做一系列操作,由于跨进程后,静态会失效,所以,EventBus 并不能跨进程。