基本用法
- 通过
@Subscribe
注解声明订阅方法,参数为定义好的事件类型
1 | (threadMode = ThreadMode.MAIN) |
- 注册订阅者
1 | EventBus.getDefault().register(this); |
- 发送事件,事件将会被传递给第一步创建的订阅方法
1 | EventBus.getDefault().post(new StringEvent("Hello")); |
- 解除订阅
1 | EventBus.getDefault().unregister(this); |
注册订阅者
注册阶段从使用上来看主要调用了两个方法:
- public static EventBus getDefault():获取默认的EventBus对象
- public void register(Object subscriber):对所有订阅方法进行订阅
获取EventBus对象
EventBus使用了典型的DCL单例模式获取默认EventBus:
1 | public static EventBus getDefault() { |
订阅类中所有订阅方法
此阶段主要分为两个两个步骤:
subscriberMethodFinder.findSubscriberMethods(subscriberClass):查找类中所有的订阅方法
subscribe(subscriber, subscriberMethod):对所有订阅方法进行订阅
1 | public void register(Object subscriber) { |
查找订阅方法
SubscriberMethodFinder首先会从缓存(METHOD_CACHE
)中获取。如果没有缓存,则根据ignoreGeneratedIndex
的值选择反射对应类获取或者直接从注解处理器生成的信息中获取:
1 | List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { |
当ignoreGeneratedIndex
为true时会通过反射的方式查找类中所有符合条件的方法(public的非static,非abstract,非bridge,非synthetic且含有Subscribe注解的方法)
1 | private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) { |
当ignoreGeneratedIndex
为false时,参考后文
订阅类中的所有方法
所有订阅方法都会被封装成一个SubscriberMethod
对象,register()
方法的最后一个步骤即为:遍历SubscribeMethod集合,使用subscibe()
方法将Subscriber与SubscribeMethod建立订阅关系:
1 | private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { |
订阅关系被描述为一个Subscription
对象,包含了订阅者以及它的所有订阅方法。所有的Subscription
按照事件类型(订阅方法的参数)分组,存储在Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType
中,组内按照订阅方法的优先级进行排序。同时,所有的事件会按照Subscriber
分组存储在Map<Object, List<Class<?>>> typesBySubscriber
中。对于粘性事件的处理参考后文
事件传递
当调用EventBus.getDefault().post(new Event())
后,事件将按照类型传递给注册的订阅者。过程如下:
- 通过
ThreadLocal
获取当前线程的状态(PostingThreadState
); - 将事件放入当前的事件队列(
PostingThreadState.eventQueue
)中; - 遍历事件队列,将所有事件传递给
postSingleEvent()
方法。
该方法主要负责取出当前线程的所有事件,并一个个交给postSingleEvent()
处理:
1 | public void post(Object event) { |
postSingleEvent()
方法主要负责确定所有需要传递的事件类型,当subscriptionFound
为true时(默认为true),EventBus会寻找EventType的所有接口和超类,并通过postSingleEventForEventType()
依次传递给相应的订阅者。如果为false,则仅传递当前事件类型。如果该事件没有订阅者,则会将该事件传递给NoSubscriberEvent
的订阅者:
1 | private void postSingleEvent(Object event, PostingThreadState postingState) throws Error { |
postSingleEventForEventType()
会通过传递过来的事件类型,从subscriptionsByEventType
中取出对应的Subscription
并交给postToSubscription()
方法处理,直到处理完所有的Subscription
或被取消:
1 | private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) { |
之前的所有方法总的来说都是对事件的细化,事件最总会由postToSubscription()
方法进行传递。根据Subscription
的threadMode
,会执行不同的传递逻辑:
- POSTING(默认模式):直接通过反射调用订阅方法,在这种模式下订阅方法的执行一定要快,否则将会阻塞剩余事件的传递;
- ASYNC:进入
AsyncPoster
的事件队列(PendingPostQueue
),通过一个异步线程去调用订阅方法;
剩余的ThreadMode
和环境有关,对于在Android中的情景:
- MAIN:如果当前为UI线程,则直接反射调用订阅方法,否则进入
mainThreadPoster
的PendingPostQueue
并通过Handler切换到UI线程去调用订阅方法; - MAIN_ORDERED:总是进入
mainThreadPoster
的PendingPostQueue
并通过Handler切换到UI线程去调用订阅方法; - BACKGROUND:如果当前为UI线程,则进入
BackgroundPoster
的PendingPostQueue
,并通过一个异步线程去调用订阅方法,否则同POSTING;
对于不在Android中的情景:
- MAIN、MAIN_ORDERED:同POSTING;
- BACKGROUND:进入
BackgroundPoster
的PendingPostQueue
,并通过一个异步线程去调用订阅方法。
对于ASYNC和BACKGROUND都会有异步线程执行的情况,但是执行过程有区别,前者对于每个事件会使用不同的线程去执行,而后者会在一个线程中执行。
1 | private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) { |
对于Poster,这里只分析mainThreadPoster
即HandlerPoster
。HandlerPoster
继承自Handler,并实现了Poster
接口,沿用了Android消息机制中的Handler
和Looper
,但消息队列是自己维护了一个PendingPostQueue
(链表结构)。当有事件传递过来时,将该事件放入队列中,并通过Handler发送一个空消息:
1 | public void enqueue(Subscription subscription, Object event) { |
空消息经过Android的消息机制的传递,会交给HandlerPoster
的handlerMessage()
方法处理。这里通过一个死循环不断从队列中取出事件,然后通过反射调用订阅方法,值得注意的是,为了避免事件太多阻塞UI线程,当执行时间大于maxMillisInsideHandleMessage
时,会结束事件的执行,并重新发送一条空消息,等到重新传递到这里时再继续处理:
1 |
|
至于AsyncPoster
和BackgroundPoster
执行过程类似,只是将Handler
换成了线程池Executors.newCachedThreadPool()
。
解除订阅
在订阅者不再使用时需要调用EventBus.unregister()
方法解除订阅,否则会造成内存泄漏。解除订阅只是简单的将订阅时添加到typesBySubscriber
和subscriptionsByEventType
中的相应数据移除:
1 | public synchronized void unregister(Object subscriber) { |