EventBus流程和工作原理
1,注册订阅者
EventBus 的创建 getDefault() 是一个双重校验锁的单例模式。建造者模式Builder,观察者模式。
2,事件发布
3,反注册订阅者
1. 注册EventBus,EventBus会获取当前类中的订阅方法,包括方法的参数,类型,优先级等信息。在获取的方法中,会先遍历缓存。如果在缓存中没有才去调用获取方法。
2. 获取订阅方法有两种方式,一种是反射,反射获取全部方法,找到加了@Subscribe注解并有一个参数的方法,另外一种是通过订阅方法索引来得到订阅方法。反射的效率较低,所以优先使用用订阅方法索引来获取,当索引列表找不到这个方法。它就会走反射的途径。注册过后这个订阅方法就被存起来了。等待接受消息。
findSubscriberMethods找出一个SubscriberMethod的集合
3. 在3.0以前是没有注解方法的那时候都是通过反射来获取和执行方法。而且方法必须以onEvent开头,分别为onEventMainThead,onEventBackground,onEvent,onEventAsync,3.0以后就全部替换为注解的形式
4. postEvent,会把这个Event加入消息队列,然后通过Event的类型信息来得到它的订阅方法,然后根据相应的订阅方式反射调用订阅方法。没有选择的threadMode的post一般都会在UI线程执行。如果当前post不是UI线程,这边会用Handle的机制来让接受Event的方法运行在UI线程。
5. 解除注册,将该对象从对象缓存列表中移除,获取当前对象的订阅列表,然后将其从订阅列表移除
---------------------
总结一下EventBus的工作原理
6.1 订阅逻辑
1、首先用register()方法注册一个订阅者
2、获取该订阅者的所有订阅的方法
3、根据该订阅者的所有订阅的事件类型,将订阅者存入到每个以 事件类型为key 以所有订阅者为values的map集合中
4、然后将订阅事件添加到以订阅者为key 以订阅者所有订阅事件为values的map集合中
5、如果是订阅了粘滞事件的订阅者,从粘滞事件缓存区获取之前发送过的粘滞事件,响应这些粘滞事件。
6.2 事件发送逻辑
1、首先获取当前线程的事件队列
2、将要发送的事件添加到事件队列中
3、根据发送事件类型获取所有的订阅者
4、根据响应方法的执行模式,在相应线程通过反射执行订阅者的订阅方法
6.3 取消逻辑
1、首先通过unregister方法拿到要取消的订阅者
2、得到该订阅者的所有订阅事件类型
3、遍历事件类型,根据每个事件类型获取到所有的订阅者集合,并从集合中删除该订阅者
4、将订阅者从步骤2的集合中移除
6.4 利与弊
EventBus好处比较明显,它能够解耦和,将业务和视图分离,代码实现比较容易。而且3.0后,我们可以通过apt预编译找到订阅者,避免了运行期间的反射处理解析,大大提高了效率。当然EventBus也会带来一些隐患和弊端,如果滥用的话会导致逻辑的分散并造成维护起来的困难。另外大量采用EventBus代码的可读性也会变差。
图是EventBus整体的运行图。事件的发布与事件的消费可能位于一个线程,也可能位于不同的线程。这取决于我们注册消费方法的时候设置的ThreadMode。
每一个线程都有一个与之关联的Queue(通过ThreadLocal办到的),事件被发布到Queue中,循环遍历Queue中的Event,并根据Event查找可以消费该事件的类(MainActivity)与方法(@Subscribe)。最终将事件交给消费方法完成一次完整的发布与消费过程。
---------------------
EventBus支持的四种线程模式(ThreadMode):
示例:
@Subscribe(threadMode = ThreadMode.POSTING)
public void eventBus(MyEvent myEvent) {
Toast.makeText(this, "呵呵哒", Toast.LENGTH_SHORT).show();
}
a)POSTING(默认):事件在哪个线程发布,就在哪个线程消费,因此要特别注意不要在UI线程进行耗时的操作,否则会ANR;
b)MAIN:事件的消费会在UI线程。因此,不宜进行耗时操作,以免引起ANR。
c)BACKGROUND:如果事件在UI线程产生,那么事件的消费会在单独的子线程中进行。否则,在同一个线程中消费。
d)ASYNC:不管是否在UI线程产生事件,都会在单独的子线程中消费事件。
另外,EventBus还支持粘性事件,即发送一个未注册的粘性事件,注册者会在完成注册之后收到这个粘性事件。