EventBus作为一个事件处理分发总线框架,如今被广泛用于大大小小的企业之中。其能够如此之火,一定有它的特别之处。今天就来分析一下EventBus的原理。
1、定义事件
2、注册、注销与发送事件
3、设置响应函数
EventBus共包括4种模式:
粘性事件
特点是不需要先订阅,就是在发送事件之后再订阅该事件也能收到该事件。例如已经发送了一个粘性事件,这时候再打开一个新的Activity或者Fragment,并在其中订阅了EventBus的消息,那么这个时候,粘性事件能够收到,而普通事件无法收到。
同一类型(消息类)的粘性事件只会保存一个,如果有多个一起发生,则只会保存最后一个类型的粘性事件
EventBus使用方法还是蛮简单的,下面让我们来看一看它的原理。
上图是发送事件时的post代码。
可以看到会获取到一个当前post线程状态的类。从里面获取一个事件队列,把事件添加到队列中。
接着会判断当前线程队列是否在发送事件,如果没有,则会把当前线程队列设置为post状态,并且把是否是主线程状态进行赋值。
接着会判断当前队列是否被取消,如果没有,则通过while循环,不断地把队列里每个事件都发送出去,每发送一个就移除一个,直到队列数量为0。
那么让我们来看一看,消息队列的持有者currentPostingThreadState是什么。
它是一个ThreadLocal,里面持有的类型是PostingThreadState。
可以看到里面的内容大致为上面说到的那几个。
这里也可以看出,当你post一个事件时,掌管这个事件的事件队列,被ThreadLocal的形式为每一个线程进行了管理。
之后再来看上面提到的队列循环里取事件发送的代码。
第一个难懂的地方,是这个lookupAllEventTypes。
假如事件为Message.class。这个方法,是把这个类当做key,为它保存了它,已经它的所有父类,以及与他相关的所有接口。这些统一放到了一个List里。
subscriptionByEventType是一个map,以事件类为key,CopyOnWriteArrayList
而这个Subscription,里面持有了一个subscriber以及subscriberMethod。
这里盲猜subscriber实际上就是你所绑定时的Activity或Fragment。
而SubscriberMethod则是在Activity或Fragment中所有使用到该类并用Suscribe注解的方法。
这里需要注意,存放的维度是以事件为维度,将Activity或Fragment与方法绑定。
而SubscriberMethod里则保存了真正的方法,线程模式,事件类型,优先级以及是否是粘性事件。
postToSubscription方法则是真正意义上的发送事件方法。
我们看到会对订阅类里的线程模式进行判断与不同的处理。
而对于不同的线程,每个都有一个自己的事件队列,如果当前线程不满足线程条件,则会被分配到对应的线程队列里去。
这个invokeSubscriber方法是最后调用我们处理事件的方法。
invoke方法包含了两个参数,第一个就是我们之前猜测的方法的持有者activity或fragment,而第二个参数正是我们处理方法所需要的参数也就是我们定义的事件类。
看到这不得不说,EventBus设计的真好。能够想到以这样的方式把Activity,事件,处理方法联系起来。
最后作者还有一个疑问,就是粘性事件是怎么工作的。
在postStickyEvent里,只比post方法多了一步,就是把事件放到了粘性事件集合中。这个集合是一个以事件类class为key,事件为value的map。这也就是为什么粘性事件发送时,只能负责触发最新的。因为之前的都被覆盖了。
而这个粘性事件集合在subscribe方法中被用到了。
其实想一下也能大概猜到,当一个activity被激活,完成事件订阅后,就会触发粘性事件。那么一定是在这里完成的。
这个订阅方法里面主要完成的就是订阅者主体与方法的绑定,同时我们看到了优先级的处理方式。当当前方法的优先级高于当前i值对应的方法优先级时,则在i的位置插入此方法,其他优先级低的事件则后移。若遍历到最后,当前方法优先级仍然是最高的,则在最后一个位置后面插入,也就是数组整体大小+1。
保存每个事件订阅内容集合的是一个CopyOnWriteArrayList,它是一个线程安全的ArrayList。适合于读多写少的场景。不能保持实时的更新。写的时候会复制出一个新的容器,往新的容器里添加元素,之后把指针指向新的容器。而读还是读的老数据,实现了读写分离。
最后,在判断此方法是否是粘性事件后,会进行一个checkPostStickyEventToSubscription操作。
这个方法的里面,调用了postToSubscription方法,实现了最终事件处理。
最后的最后,要搞清楚subscribe方法是在什么时机调用的。
啊哈,最后在这里看到了我们想要的答案。
也就是这一切的一切,都从register的那一刻,开始了。