EventBus流程及原理

EventBus原理

设计模式:单例模式、建造者模式、观察者模式

单例:
image
建造者:
image

register:

流程图:
register
源码:
public void register(Object subscriber) {

// 用 subscriberMethodFinder 提供的方法,找到在 subscriber 这个类里面,订阅的内容。

Class subscriberClass = subscriber.getClass();

    List subscriberMethods =subscriberMethodFinder.findSubscriberMethods(subscriberClass);

    synchronized (this) {

for (SubscriberMethod subscriberMethod : subscriberMethods) {

// 遍历这些方法,subscribe 这些事件

subscribe(subscriber, subscriberMethod);

        }

}

}
subscribe():生成3个集合类
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    //获取subsrciberMethod传递的自定义EventType参数的运行时的类
    Class eventType = subscriberMethod.eventType;
    //Subscription用于绑定subscriber和sucriberMethod,一个订阅者可以有多个subscriberMethod
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    //根据EventType的运行时类取到该类所有的subscriptioins,subscriptionsByEventType是HashMap中的key
    CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);
    if (subscriptions == null) {
         subscriptions = new CopyOnWriteArrayList<>();
         //若根据EventType找不到subscriptions,则eventType作key,subscriptions作value添加到subscriptionByEventType中。
         subscriptionsByEventType.put(eventType, subscriptions);
    } else {
         if (subscriptions.contains(newSubscription)) {
         //已经存在newSubscription,抛出异常该订阅者已经注册,不可重复注册同一个subscriber
             throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                     + eventType);
         }
    }

    int size = subscriptions.size();
    for (int i = 0; i <= size; i++) {
        //循环subscriptions,根据标记优先级的priority从高到低,将新的subscription插入到subscriptions中
        if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
            subscriptions.add(i, newSubscription);
            break;
        }
    }
    //typesBySubscriber是一个HashMap,根据subscriber做key,获取该subscriber对应的所有的订阅事件的类型
    List> subscribedEvents = typesBySubscriber.get(subscriber);
      if (subscribedEvents == null) {
          subscribedEvents = new ArrayList<>();
          //该订阅者之前的订阅事件类型列表为空,则将当前订阅类型添加到typesBySubscriber中
          typesBySubscriber.put(subscriber, subscribedEvents);
      }
    subscribedEvents.add(eventType);
    //如果该方法被标识为sticky事件
    if (subscriberMethod.sticky) {
         if (eventInheritance) { eventInheritance标识是否考虑EventType的类层次结构
              //循环所有的sticky黏性事件
              Set, Object>> entries = stickyEvents.entrySet();
              for (Map.Entry, Object> entry : entries) {
                  Class candidateEventType = entry.getKey();
                  //如果当前事件是其他事件的同类型的或者是他们的父类
                  if (eventType.isAssignableFrom(candidateEventType)) {
                     Object stickyEvent = entry.getValue();
                     heckPostStickyEventToSubscription(newSubscription, stickyEvent);
                  }
              }
         } else {
             Object stickyEvent = stickyEvents.get(eventType);
             checkPostStickyEventToSubscription(newSubscription, stickyEvent);
         }
    }
}
  1. Map<Class<?>,CopyOnWriteArrayList<Subscription>> subscriptionByEventType -> Key:Event,Value:Subscribe集合,当发送这个Event时,可在Map中找到相应的Subscriber集合,将订阅方法可接收时间类型和对应的Subscription集合保存到subscriptionsByEventType中去。

2. Map<Object,List<Class<?>>> typeBySubscriber -> Key:Subscriber,Value:Event,当注册和反注册时,都会操作typesBySubscriber,将注册类对象和注册类中订阅事件方法的参数类型集合保存到typesBySubscriber。

3. Map, Object>stickyEvents -> 如果是粘性事件subscriberMethod.sticky=true,则从stickyEvent中取出对应类型时间,用迭代器一一进行执行订阅事件的方法。

订阅的方法信息
public SubscriberMethod(String methodName, Class<?> eventType, ThreadMode threadMode,
                                int priority, boolean sticky) {
        this.methodName = methodName;
        this.threadMode = threadMode;
        this.eventType = eventType;
        this.priority = priority;
        this.sticky = sticky;
    }
findSubscriberMethods 这个方法是实现 EventBus 的核心代码,这里面包含了 EventBus 隐式定义的交互协议。从这个方法里面,可以看到如何争取地使用EventBus。
 List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { {

// 如果这个 Class 对应的方法被缓存,直接返回。

List subscriberMethods =METHOD_CACHE.get(subscriberClass);

    if (subscriberMethods !=null) {

return subscriberMethods;

    }

//ignoreGeneratedIndex默认为false,是否从索引勒种取出当前注册类对应的事件订阅方法信息

if (ignoreGeneratedIndex) {

subscriberMethods = findUsingReflection(subscriberClass);//使用反射去查找

    }else {

subscriberMethods = findUsingInfo(subscriberClass);//使用索引去查找

    }

if (subscriberMethods.isEmpty()) {//空就抛异常,不空加进缓存中

throw new EventBusException("Subscriber " + subscriberClass

+" and its super classes have no public methods with the @Subscribe annotation");

    }else {

METHOD_CACHE.put(subscriberClass, subscriberMethods);

        return subscriberMethods;

    }

}


post&postSticky:

流程图:
post&postSticky
postSticky:
/**

* Posts the given event to the event bus and holds on to the event (because it is sticky). The most recent sticky

* event of an event's type is kept in memory for future access by subscribers using {@link Subscribe#sticky()}.

*/

public void postSticky(Object event) {

    synchronized (stickyEvents) {

        stickyEvents.put(event.getClass(), event);//加入粘性事件stickyEvents:Map中

    }

  // Should be posted after it is putted, in case the subscriber wants to remove immediately

    post(event);

}
######post:
/** Posts the given event to the event bus. */

public void post(Object event) {

  //从currentPostingThreadState:ThreadLocal中获取发送线程的状态

    PostingThreadState postingState =currentPostingThreadState.get();

    List eventQueue = postingState.eventQueue; //从获取发送线程中队列

    eventQueue.add(event);//将事件加入到发送线程中队列

    if (!postingState.isPosting) {//如果没有发送就开始去发送

        postingState.isMainThread = isMainThread();

        postingState.isPosting =true;

        if (postingState.canceled) {//如果取消了就抛出异常

          throw new EventBusException("Internal error. Abort state was not reset");

        }

        try {

              while (!eventQueue.isEmpty()) {//循环从队列中拿取第一个事件,以及它的发送状态信息进行单个的发送

                  postSingleEvent(eventQueue.remove(0), postingState);

            }

        }finally {

                //重置发送状态信息

            postingState.isPosting =false;

            postingState.isMainThread =false;

        }

    }

}

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {

        /*获取事件的Class,所有事件的Class对应的订阅者列表在register的时候是已经保存了的*/

        Class<?> eventClass = event.getClass();

        /*根据事件的Class找到订阅者的标志状态,初始化为false*/

        boolean subscriptionFound = false;

        /*

        * 比如 A extends B implements C  发布者post(A),那么找订阅者的时候不仅要找订阅了事件A的订阅者

        * 还要找订阅了B和C的订阅者*/

        if (eventInheritance) {

            /*找到事件的所有父类和所有实现的接口*/

            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);

            int countTypes = eventTypes.size();

            for (int h = 0; h < countTypes; h++) {

                Class<?> clazz = eventTypes.get(h);

                subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);

            }

            /*不考虑事件的继承性的话,那么处理起来就比较简单了*/

        } else {

            subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);

        }

     if (!subscriptionFound) {

       if (logNoSubscriberMessages) {

             Log.d(TAG, "No subscribers registered for event " + eventClass);

        }

        if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&

                 eventClass != SubscriberExceptionEvent.class) {

                post(new NoSubscriberEvent(this, event));

     }
   }
}

前边说过了EventBus维护了两个重要的map。其中一个就是事件的Class到能处理此事件的所有订阅者列表的map,因此不仅要把事件的Class存入map中的订阅者列表,还要把事件的所有父类和所有实现的接口的Class存入订阅者列表。下边是找到某个类所有父类和所有实现接口的方法:

/** Looks up all Class objects including super classes and interfaces. Should also work for interfaces. */

    /*找到事件的所有父类和实现的接口,以Class列表的形式返回*/

    private List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {

        /*private static final Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<Class<?>, List<Class<?>>>();*/

        synchronized (eventTypesCache) {

            /*查看缓存,看能否命中*/

            List<Class<?>> eventTypes = eventTypesCache.get(eventClass);

            /*缓存不命中*/

            if (eventTypes == null) {

                /*创建事件类型列表*/

                eventTypes = new ArrayList<Class<?>>();

                Class<?> clazz = eventClass;

                /*getSuperclass()返回null的情况:Class表示的类为Object、基本数据类型、接口或者void*/

                while (clazz != null) {

                    eventTypes.add(clazz);

                    /*把接口对应的Class也添加进Class列表

                    * clazz.getInterfaces():返回clazz表示的类直接实现的接口的Class列表,不包括间接实现的接口

                    * */

                    addInterfaces(eventTypes, clazz.getInterfaces());

                    /** public Class<? super T> getSuperclass() :

                    * Returns the Class object which represents the superclass of the

                    * class represented by this  Class. If this  Class represents

                    * the Object class, a primitive type, an interface or void then the

                    * method returns null. If this Class represents an array

                    * class then the  Object class is returned.

                    */

                    clazz = clazz.getSuperclass();

                }

                /*找到一个事件的所有父类和所有实现接口的Class挺复杂的,循环加递归的,还是加入缓存机制提高性能吧*/

                eventTypesCache.put(eventClass, eventTypes);

            }

            return eventTypes;

        }

    }

    /** Recurses through super interfaces. 获取某个类直接实现的所有接口,包括间接实现的

    * @param eventTypes

    * @param interfaces*/

    static void addInterfaces(List<Class<?>> eventTypes, Class<?>[] interfaces) {

        /*对这个类直接实现的每个接口开始遍历*/

        for (Class<?> interfaceClass : interfaces) {

            /*如果这个接口还没有添加到列表接把他添加到列表*/

            if (!eventTypes.contains(interfaceClass)) {

                eventTypes.add(interfaceClass);

                /*把这个接口所直接实现的所有接口的Class也添加进列表 递归调用*/

                addInterfaces(eventTypes, interfaceClass.getInterfaces());

            }

        }

    }
最后调用
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
        if (stickyEvent != null) {
            // If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
            // --> Strange corner case, which we don't take care of here.
            postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());
        }
    }

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
       //根据@subscriber中threadMode进行区分,POSTING为当前线程执行,
       //MAIN为主线程,BACKGROUND为子进程,ASYNC为异步执行。
       switch (subscription.subscriberMethod.threadMode) {
           case POSTING:
               invokeSubscriber(subscription, event);
               break;
           case MAIN:
               if (isMainThread) {
                   invokeSubscriber(subscription, event);
               } else {
                   mainThreadPoster.enqueue(subscription, event);
               }
               break;
           case BACKGROUND:
               if (isMainThread) {
                   backgroundPoster.enqueue(subscription, event);
               } else {
                   invokeSubscriber(subscription, event);
               }
               break;
           case ASYNC:
               asyncPoster.enqueue(subscription, event);
               break;
           default:
               throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
       }
   }

void invokeSubscriber(Subscription subscription, Object event) {
        try {
            subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
        } catch (InvocationTargetException e) {
            handleSubscriberException(subscription, event, e.getCause());
        } catch (IllegalAccessException e) {
            throw new IllegalStateException("Unexpected exception", e);
        }
    }

unregister:

流程图:
unregister
源码:
/** Unregisters the given subscriber from all event classes. */
    public synchronized void unregister(Object subscriber) {
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
            for (Class<?> eventType : subscribedTypes) {
                unsubscribeByEventType(subscriber, eventType);
            }
            typesBySubscriber.remove(subscriber);
        } else {
            logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }

/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
    private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
        List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions != null) {
            int size = subscriptions.size();
            for (int i = 0; i < size; i++) {
                Subscription subscription = subscriptions.get(i);
                if (subscription.subscriber == subscriber) {
                    subscription.active = false;
                    subscriptions.remove(i);
                    i--;
                    size--;
                }
            }
        }
    }

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350