EventBus学习心得

来源:互联网 发布:如何补充胶原蛋白知乎 编辑:程序博客网 时间:2024/05/16 15:00

一.什么是EventBus
最近面试的时候经常被问到fragment之间的通信,当我回答使用接口模式的时候,面试官就问我有没有用过EventBus,当时我是懵逼的。回来以后赶紧恶补一下,看了以后发现自己确实OUT了。
EventBus说白了就是一个发布/订阅的事件总线,主要功能是替代Intent,Handler,BroadCast在Fragment,Activity,Service,线程之间传递消息.优点是开销小,代码更优雅。以及将发送者和接收者解耦。他有四个成分:发布者,订阅者,事件和总线。订阅者订阅事件到总线,发布者发布事件。

二.EventBus的订阅
EventBus在Fragment或者Activity的onCreate()方法中订阅事件:EventBus.getDefault().register(this),在onDestroy方法中取消订阅:EventBus.getDefault().unregister(this);注意,当加入这两个方法后,当前的Fragment或Activity中必须有onEvent开头的执行方法,不然会报异常,你不能只去订阅事件,而不去接受事件。订阅事件的原理是你遍历当前Fragment或Activity中的所有onEvent开头的方法,然后根据参数类型来判断具体执行哪个方法,实现原理在源码里都有解释,核心代码如下:
1.得到所有的方法:Method[] methods = clazz.getMethods();
2.遍历每一个方法了,去匹配封装了(源码SubscriberMethodFinder 76-116行):

for (Method method : methods) {....}lazz = clazz.getSuperclass();

3.分别判断了是否以onEvent开头,是否是public且非static和abstract方法,是否是一个参数。如果都复合,才进入封装的部分:

 if (methodName.startsWith(ON_EVENT_METHOD_NAME)) {    int modifiers = method.getModifiers();    if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers &          MODIFIERS_IGNORE) == 0) {       Class<?>[] parameterTypes = method.getParameterTypes();       if (parameterTypes.length == 1) 

4.封装完后将method, threadMode, eventType传入构造了:new SubscriberMethod(method, threadMode, eventType)。添加到List,最终放回。
5.注意会扫描所有的父类,不仅仅是当前类:clazz = clazz.getSuperclass();
6.根据subscriberMethod.eventType,去subscriptionsByEventType去查找一个CopyOnWriteArrayList ,如果没有则创建。
顺便把我们的传入的参数封装成了一个:Subscription(subscriber, subscriberMethod, priority。这里的subscriptionsByEventType是个Map,key:eventType ; value:CopyOnWriteArrayList ; 这个Map其实就是EventBus存储方法的地方,一定要记住!
7.添加newSubscription;并且是按照优先级添加的。可以看到,优先级越高,会插到在当前List的前面。
8.根据subscriber存储它所有的eventType ; 依然是map;key:subscriber ,value:List ;知道就行,非核心代码,主要用于isRegister的判断。
9.判断sticky;如果为true,从stickyEvents中根据eventType去查找有没有stickyEvent,如果有则立即发布去执行。stickyEvent其实就是我们post时的参数。

小结:你只要记得一件事:扫描了所有的方法,把匹配的方法最终保存在subscriptionsByEventType(Map,key:eventType ; value:CopyOnWriteArrayList )中;
eventType是我们方法参数的Class,Subscription中则保存着subscriber, subscriberMethod(method, threadMode, eventType), priority;包含了执行改方法所需的一切。

三.EventBus的发布
发布事件就更简单了,只需要执行EventBus.getDefault().post(object),其中object为你所需要传的参数。
了解了EventBus使用原理后,我就开始疑惑,如果多个地方同时发布同一参数类型的事件时,订阅事件遍历会不会冲突,是执行其中一个还是都执行,答案是都执行,发布几次就执行几次,那会不会冲突呢?。还是看源码:
1.把我们传入的event,保存到了当前线程中的一个变量PostingThreadState的eventQueue中。
2.判断当前是否是UI线程。
3.遍历队列中的所有的event,调用postSingleEvent(eventQueue.remove(0), postingState)方法。
这里,post是去调用整个队列,可能会造成方法多次调用,为了防止这种情况,源码中有个判断,就是防止该问题的,isPosting=true了,就不会往下走了。

之前订阅事件(register)会把当前类中匹配的方法,存入一个map,而post会根据实参去map查找进行反射调用。

反射调用:
直接反射调用;也就是说在当前的线程直接调用该方法;
1.case MainThread:首先去判断当前如果是UI线程,则直接调用;否则: mainThreadPoster.enqueue(subscription, event);把当前的方法加入到队列,然后直接通过handler去发送一个消息,在handler的handleMessage中,去执行我们的方法。说白了就是通过Handler去发送消息,然后执行的。
2. case BackgroundThread:如果当前非UI线程,则直接调用;如果是UI线程,则将任务加入到后台的一个队列,最终由Eventbus中的一个线程池去调用executorService = Executors.newCachedThreadPool();。
3. case Async:将任务加入到后台的一个队列,最终由Eventbus中的一个线程池去调用;线程池与BackgroundThread用的是同一个。
这么说BackgroundThread和Async有什么区别呢?BackgroundThread中的任务,一个接着一个去调用,中间使用了一个布尔型变量handlerActive进行的控制。Async则会动态控制并发

0 0
原创粉丝点击