一文完全搞懂EventBus 3.0原理

1.簡介

EventBus是一種用於Android的事件發佈-訂閱總線框架,由GreenRobot開發,Gihub地址是:EventBus。它簡化了應用程序內各個組件之間進行通訊的複雜度,尤爲是碎片之間進行通訊的問題,能夠避免因爲使用廣播通訊而帶來的諸多不便。java


2.Subscribe註解

自3.0開始,訂閱事件的方法開始使用了Subscribe註解,再也不使用方法名了,如如下方式git

@Subscribe
public void testEventBus(Object obj){
    ...
}
複製代碼

看下註解github

@Documented
@Retention(RetentionPolicy.RUNTIME) // 註解不只被保存到class文件中,jvm加載class文件以後,仍然存在
@Target({ElementType.METHOD})  // 做用在方法上
public @interface Subscribe {

    // 指定事件訂閱方法所在的線程模式,也就是決定訂閱方法是在哪一個線程,默認是POSTING模式
    ThreadMode threadMode() default ThreadMode.POSTING;

    // 是否支持粘性事件
    boolean sticky() default false;

    // 優先級,若是指定了優先級,則若干方法接收同一事件時,優先級高的方法會先接收到。
    int priority() default 0;
}
複製代碼

ThreadMode能夠指定的模式有:緩存

  1. ThreadMode.POSTING:默認的線程模式,在哪一個線程發送事件就在對應線程處理事件,避免了線程切換,效率高。
  2. ThreadMode.MAIN:如在主線程(UI線程)發送事件,則直接在主線程處理事件;若是在子線程發送事件,則先將事件入隊列,而後經過 Handler 切換到主線程,依次處理事件。
  3. ThreadMode.MAIN_ORDERED:不管在哪一個線程發送事件,都將事件加入到隊列中,而後經過Handler切換到主線程,依次處理事件。
  4. ThreadMode.BACKGROUND:與ThreadMode.MAIN相反,若是在子線程發送事件,則直接在子線程處理事件;若是在主線程上發送事件,則先將事件入隊列,而後經過線程池處理事件。
  5. ThreadMode.ASYNC:與ThreadMode.MAIN_ORDERED相反,不管在哪一個線程發送事件,都將事件加入到隊列中,而後經過線程池執行事件

3.register註冊

好了,要想使用Eventbus,則要先註冊它,看看如何使用框架

EventBus.getDefault().register(this);
複製代碼

很簡單吧,getDefault()其實就是一個單例模式,建立EventBus實例對象,並返回jvm

public static EventBus getDefault() {
        
    if (defaultInstance == null) {
         synchronized (EventBus.class) {
             if (defaultInstance == null) {
                 defaultInstance = new EventBus();
             }
         }
    }
    return defaultInstance;
}
複製代碼

沒啥可說的,繼續看registerasync

先看圖ide

在這裏插入圖片描述

再看代碼post

public void register(Object subscriber) {
     Class<?> subscriberClass = subscriber.getClass(); // 獲取傳入的要註冊類的字節碼文件
     List<SubscriberMethod> subscriberMethods = 
     subscriberMethodFinder.findSubscriberMethods(subscriberClass); // ->>分析1
     
     synchronized (this) {

         // 遍歷訂閱方法封裝類的集合
         for (SubscriberMethod subscriberMethod : subscriberMethods) {
              subscribe(subscriber, subscriberMethod); // ->> 分析4
         }
     }
}
複製代碼

從上面的圖能夠看出,這個方法其實就是作了2件事ui

  1. 根據註冊類的字節碼文件,調用findSubscriberMethods方法,獲取該註冊類上的全部訂閱方法的信息集合。
  2. 遍歷這個信息集合,給2個map填充數據: subscriptionsByEventType能夠根據event(事件類型,訂閱方法上的參數類型)獲取全部訂閱方法信息集合。 typesBySubscriber能夠根據這個註冊類,獲取這個註冊類上全部的event事件類型。
/** * 分析1:findSubscriberMethods() * 做用:獲取當前要進行註冊類中的全部訂閱方法,也就是找尋使用了Subscribe註解、有public修飾符、一個參數的方法 */
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {

    // METHOD_CACHE: 是一個ConcurrentHashMap,key是要註冊類的字節碼文件,value是這個字節碼文件裏的全部訂閱方法信息的集合,集合的元素是SubscriberMethod,它實際上就是訂閱方法的信息類,包含Method對象、線程模式、事件類型、優先級、是不是粘性事等。
    List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass); // 這步實際上就是看看這個註冊類的方法是否已經緩存了,緩存過就直接根據類返回
    if (subscriberMethods != null) {
        return subscriberMethods;
    }

    // EventBus是支持EventBusBuilder的,若是咱們自定義了EventBusBuilder,則ignoreGeneratedIndex爲true,不然爲false,咱們沒自定義,全部看false
    if (ignoreGeneratedIndex) {
        subscriberMethods = findUsingReflection(subscriberClass);
    } else {

        // ->>分析2
        subscriberMethods = findUsingInfo(subscriberClass);
    }
    
    // 若是該類沒有找到訂閱方法,拋出異常
    if (subscriberMethods.isEmpty()) {
        throw new EventBusException("Subscriber " + subscriberClass + " and its super classes have no public methods with the @Subscribe annotation");
    } else {
 
        // 將該註冊類的類型爲key, 將這個類全部註冊方法的封裝類集合爲value存入map集合
        METHOD_CACHE.put(subscriberClass, subscriberMethods);
        return subscriberMethods;

        // ->> 返回register()方法中
    }
}
複製代碼

這個方法主要做用就是根據傳入的註冊類返回該類上全部的訂閱方法的信息,先找緩存METHOD_CACHE,有就走緩存,沒有就調用findUsingInfo方法獲取訂閱方法信息集合,而後再根據註冊類爲key, 訂閱方法的信息集合爲value, 存入緩存(METHOD_CACHE)中。

/** * 分析2:findUsingInfo() * 做用:若是findState緩存了,訂閱方法信息,則使用findState裏的緩存,不然調用findUsingReflectionInSingleClass方法,反射獲取訂閱方法信息。 */
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
  
    // FindState輔助咱們查找訂閱方法的類,後面會講述
    FindState findState = prepareFindState();
    findState.initForSubscriber(subscriberClass);

    // findState.clazz就是咱們的註冊類subscriberClass
    while (findState.clazz != null) {

        findState.subscriberInfo = getSubscriberInfo(findState);

        // 該類第一次註冊時,findState.subscriberInfo爲null, 咱們走false
        if (findState.subscriberInfo != null) {
                SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
                for (SubscriberMethod subscriberMethod : array) {
                    if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                        findState.subscriberMethods.add(subscriberMethod);
                    }
                }
        } else {

            // ->> 分析3
            findUsingReflectionInSingleClass(findState);
        }

        // 修改findState.clazz爲subscriberClass的父類Class,即須要遍歷父類
        findState.moveToSuperclass();
    }

     // 將查找到的方法保存在了FindState實例的subscriberMethods集合中。而後使用subscriberMethods構建一個新的List<SubscriberMethod>並返回,最後釋放掉findState
    return getMethodsAndRelease(findState);

    // ->> 返回到findSubscriberMethods() 方法中
}
複製代碼
/** * 分析3:findUsingReflectionInSingleClass() * 做用:經過反射獲取訂閱方法的信息 */
private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
         // 經過反射獲取訂閱類中的全部方法
         methods = findState.clazz.getDeclaredMethods();
     } catch (Throwable th) {
         ...
     }

     // 遍歷方法
     for (Method method : methods) {

        // 獲取方法修飾符
        int modifiers = method.getModifiers();

        // 方法是public類型,但非abstract、static等
        if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
            
            // 獲取方法的修飾類型
            Class<?>[] parameterTypes = method.getParameterTypes();
            
            // 只能是1個參數
            if (parameterTypes.length == 1) {

                // 獲取方法上的名爲Subscribe的註解
                Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                
                // 若是該方法帶Subscribe註解
                if (subscribeAnnotation != null) {
                       
                    // 獲取該訂閱方法上的第一個參數類型,也就是訂閱的事件類型
                    Class<?> eventType = parameterTypes[0];
                     
                    // checkAdd()方法用來判斷FindState中是否已經添加過將該事件類型爲key的鍵值對,沒添加過則返回true
                    if (findState.checkAdd(method, eventType)) {
                            
                        // 獲取線程模式
                        ThreadMode threadMode = subscribeAnnotation.threadMode();

                        // 將該訂閱方法,事件類型,線程模式,優先級,是否支持粘性事件等信息,封裝成SubscriberMethod對象,並添加到findState中的subscriberMethods集合裏
                        findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode, subscribeAnnotation.priority(), subscribeAnnotation.sticky()));

                         // ->> 返回到findUsingInfo() 方法中
                    }
                }
            } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                ...
            }
        } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
            ...
        }
    }
}
複製代碼

根據反射,獲取訂閱方法的信息數據,而後將它分封裝成SubscriberMethod對象,並添加到findState的集合中。

/** * 分析4:subscribe() * 做用:主要就是構建2個map對象 */
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    
    // 獲取該訂閱方法的事件類型
    Class<?> eventType = subscriberMethod.eventType;
        
    // 將訂閱方法的封裝類,再進行封裝,也就是註冊類的信息也存入了 
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    
    // subscriptionsByEventType是hashmap, 以事件類型爲key, Subscription集合爲value
    // 先查找subscriptionsByEventType是否存在以當前事件類型爲key的值
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);

    // 若是沒有的話 
    if (subscriptions == null) {

        // 建立集合,根據事件類型,合併數據
        subscriptions = new CopyOnWriteArrayList<>();
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {
        if (subscriptions.contains(newSubscription)) {
            throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType);
        }
    }

    // 添加上邊建立的newSubscription對象到subscriptions中
    int size = subscriptions.size();
    for (int i = 0; i <= size; i++) {
  
        // 根據優先級進行排序
        if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
            subscriptions.add(i, newSubscription);
            break;
        }
    }
 
    // typesBySubscriber也是一個HashMap,保存了以當前要註冊類的對象爲key,註冊類中訂閱事件的方法的參數類型的集合爲value的鍵值對
    // 和上面同樣,根據key先判斷,是否已經存儲過了,若是已經存儲過了,直接取出訂註冊類中訂閱事件的方法的參數類型的集合
    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
    if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    subscribedEvents.add(eventType);

    // 是否支持粘性事件
    if (subscriberMethod.sticky) {
        
        // ->> 分析5
        ...
    }
複製代碼

2個map構建完畢了,咱們的註冊也就完事了

總結一下

傳入註冊類信息,根據反射獲取註冊類上的全部方法,遍歷這些方法,取出其中的訂閱方法(條件是,一個參數,權限爲public,使用了Subscribe標籤)將方法的信息封裝成SubscriberMethod對象,並存入集合,而後再遍歷這個集合,取出其中的SubscriberMethod對象,再根據註冊類的字節碼文件,合併成Subscription對象,再根據event類型,進行從新分類,存入map subscriptionsByEventType中(key 爲event, value 爲List),再建立map typesBySubscriber, 註冊類爲key , list爲value。 完事了。

4.unregister取消註冊

使用很簡單

EventBus.getDefault().unregister(this);
複製代碼

看圖

看下代碼

public synchronized void unregister(Object subscriber) {

    // ->> 分析6
    List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
    
    // 若是集合不爲null 
    if (subscribedTypes != null) {
            
         // 遍歷集合,獲取訂閱事件的類型
         for (Class<?> eventType : subscribedTypes) {
              
              // ->> 分析7
              unsubscribeByEventType(subscriber, eventType);
         }
         typesBySubscriber.remove(subscriber);
     } else {
          logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
     }
}
複製代碼

分析6:還記得咱們分析註冊時,建立的那2個map嗎? 其中一個是typesBySubscriber,key是註冊類,value是事件類型的集合(List), 這一步就是根據註冊類獲取該類全部訂閱方法的事件類型。

/** * 分析7 */
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
        
    // 根據事件類型,獲取該事件類型所對應的訂閱方法信息的集合
    List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    
    // 若是集合不爲null
    if (subscriptions != null) {
            
        // 遍歷,該事件類型所對應的訂閱方法
        int size = subscriptions.size();
        for (int i = 0; i < size; i++) {
        
             // 獲取Subscription對象,該對象包含了訂閱方法的全部信息和註冊類信息
             Subscription subscription = subscriptions.get(i);
             
             // 由於subscriptionsByEventType可不光包含了1個註冊類的信息,因此要加下面的判讀,若是該訂閱方法所在的註冊類是咱們要解除的註冊類的話
             if (subscription.subscriber == subscriber) {
                 subscription.active = false;

                 // 從集合中,將該訂閱方法的信息刪除掉
                 subscriptions.remove(i);
                 i--;
                 size--;
             }
        }
    }
}
複製代碼

總結一下

解除綁定,其實比較簡單,主要就是運用註冊時所產生的2個map, 先根據typesBySubscriber,也就是根據要解除綁定的註冊類,找到這個類所擁有的全部訂閱事件,而後遍歷這些訂閱事件,再根據這些訂閱事件,在subscriptionsByEventType中找到,這個事件所對應的訂閱方法的集合,再遍歷集合,判斷該訂閱方法的註冊類信息,是不是要解除綁定的註冊類,若是是,移除該訂閱方法信息,完成解除綁定。

4.post發佈事件

使用也很簡單

EventBus.getDefault().post(new Object());
複製代碼

看圖

看代碼

public void post(Object event) {
       
    // ->> 分析8
    PostingThreadState postingState = currentPostingThreadState.get();
    
    // 獲取postingState裏面存的一個隊列
    List<Object> 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()) {
             
                 // ->> 分析9
                 postSingleEvent(eventQueue.remove(0), postingState);
             }
        } finally {
            
            // 重置狀態
            postingState.isPosting = false;
            postingState.isMainThread = false;
        }
    }
}
複製代碼

分析8:postingState實際就是一個線程狀態的封裝類,包含事件隊列,線程狀態,是否正在發送的標識位,Subscription等信息,currentPostingThreadState爲ThreadLocal,這也就說明postingState爲線程獨有的,不會讓其餘線程共享當前線程的數據

post() 方法主要就是要先將發送的事件保存在postingState中的隊列裏面,它是線程獨有的,而後經過循環隊列,將事件交給postSingleEvent()方法處理。

/** * 分析9 */
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    Class<?> eventClass = event.getClass();
    boolean subscriptionFound = false;

    // 是否要查看全部的繼承關係
    if (eventInheritance) {
           
         // 經過lookupAllEventTypes()拿到該事件全部的父類事件類型
         List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
         int countTypes = eventTypes.size();

         // 遍歷事件類型
         for (int h = 0; h < countTypes; h++) {
             Class<?> clazz = eventTypes.get(h);

             // ->> 分析10
             subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
        }
    } else {
        subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
    }
     
    if (!subscriptionFound) {
           
        if (logNoSubscriberMessages) {
            logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
        }
            
        // 若是咱們沒有訂閱事件,則發送NoSubscriberEvent
        if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class && eventClass != SubscriberExceptionEvent.class) {
            post(new NoSubscriberEvent(this, event));
        }
    }
}
複製代碼

postSingleEvent()方法中,根據eventInheritance屬性,決定是否向上遍歷事件的父類型,而後用postSingleEventForEventType()方法進一步處理事件。

/** * 分析10 */
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {

    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {

        // 還記得註冊時構建的map subscriptionsByEventType嗎?對,這步就是根據事件類型,獲取它所對應的List<subscription>也就是訂閱方法集合
        subscriptions = subscriptionsByEventType.get(eventClass);
    }
    
    // 若是集合不爲空
    if (subscriptions != null && !subscriptions.isEmpty()) {
        
        // 遍歷集合,取出Subscription(訂閱方法信息包裝類) 
        for (Subscription subscription : subscriptions) {
                
            // 記錄事件
            postingState.event = event;
            postingState.subscription = subscription;
            boolean aborted = false;
            try {
 
                // 處理事件 ->> 分析11
                postToSubscription(subscription, event, postingState.isMainThread);
                aborted = postingState.canceled;
            } finally {
                postingState.event = null;
                postingState.subscription = null;
                postingState.canceled = false;
            }
            if (aborted) {
                break;
            }
        }
        return true;
    }
    return false;
}
複製代碼

這個方法其實很簡單,就是根據事件類型,在subscriptionsByEventType中找到對應的訂閱方法信息的集合,而後遍歷集合,拿到訂閱方法信息的封裝類,調用postToSubscription去執行。

/** * 分析11 */
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {

    // 根據訂閱方法設置的線程模式去執行
    switch (subscription.subscriberMethod.threadMode) {
    
        // 默認線程模式,在哪一個線程發送事件,就在哪一個線程接收事件
        case POSTING:

            // ->> 分析12
            invokeSubscriber(subscription, event);
            break;

        // 若是是主線程,則直接執行,子線程加入隊列,而後經過 Handler 切換到主線程執行
        case MAIN:
            if (isMainThread) {

                // 主線程,直接反射執行
                invokeSubscriber(subscription, event);
            } else {

                // ->> 分析13
                mainThreadPoster.enqueue(subscription, event);
            }
            break;

        // 不管在哪一個線程,都加隊列,經過handler 在主線程執行
        case MAIN_ORDERED:
            
            // ->> 分析13
            if (mainThreadPoster != null) {
                mainThreadPoster.enqueue(subscription, event);
            } else {
                invokeSubscriber(subscription, event);
            }
            break;

        // 若是在子線程中,直接執行,若是在主線程中,加入隊列,經過線程池執行
        case BACKGROUND:
                
            if (isMainThread) {

                // ->> 分析15
                backgroundPoster.enqueue(subscription, event);
            } else {
 
                // 在子線程,直接反射執行
                invokeSubscriber(subscription, event);
            }
            break;

        // 不管在哪一個線程執行,都加入隊列,用線程池執行
        case ASYNC:
             
             // AsyncPoster和backgroundPoster類型,可是AsyncPoster沒有加同步鎖,這也就形成了,它每次執行一個任務,都會開一個子線程,而backgroundPoster不會
             asyncPoster.enqueue(subscription, event);
             break;
        default:
             throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
    }
}

/** * 分析12:直接經過反射調用執行 */
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);
    }
}

/** * 分析13:mainThreadPoster爲HandlerPoster, 具體分析下HandlerPoster */
 public class HandlerPoster extends Handler implements Poster {
 
    private final PendingPostQueue queue;
    private boolean handlerActive;
    ......
    public void enqueue(Subscription subscription, Object event) {
        // 用subscription和event封裝一個PendingPost對象
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            // 加入到隊列中
            queue.enqueue(pendingPost);
            if (!handlerActive) {
                handlerActive = true;
                
                // sendMessage()發送處理事件的消息,handleMessage()方法將被執行,將子線程切換到主線程
                if (!sendMessage(obtainMessage())) {
                    throw new EventBusException("Could not send handler message");
                }
            }
        }
    }

    @Override
    public void handleMessage(Message msg) {
        boolean rescheduled = false;
        try {
            long started = SystemClock.uptimeMillis();
            // 遍歷隊列
            while (true) {
                // 出隊列,取出PendingPost對象
                PendingPost pendingPost = queue.poll();
                ...
                // ->> 分析14
                eventBus.invokeSubscriber(pendingPost);
                ...
            }
        } finally {
            handlerActive = rescheduled;
        }
    }
}

/** * 分析14:進一步處理PendingPost對象 */
void invokeSubscriber(PendingPost pendingPost) {
     
    // 取出事件類型
    Object event = pendingPost.event;

    // 取出訂閱方法的信息封裝類
    Subscription subscription = pendingPost.subscription;

    // 釋放pendingPost引用的資源
    PendingPost.releasePendingPost(pendingPost);
    if (subscription.active) {

        // 經過反射調用執行該訂閱方法
        invokeSubscriber(subscription, event);
    }
}

/** * 分析15 */
final class BackgroundPoster implements Runnable, Poster {

    private final PendingPostQueue queue;
    private final EventBus eventBus;

    private volatile boolean executorRunning;

    BackgroundPoster(EventBus eventBus) {
        this.eventBus = eventBus;
        queue = new PendingPostQueue();
    }

    public void enqueue(Subscription subscription, Object event) {

        // 用subscription和event封裝一個PendingPost對象
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {

            // 加入隊列
            queue.enqueue(pendingPost);
            if (!executorRunning) {
                executorRunning = true;

                // 調用newCachedThreadPool線程池,執行任務
                eventBus.getExecutorService().execute(this);
            }
        }
    }

    @Override
    public void run() {
        try {
            try {

                // 循環隊列
                while (true) {
                    
                    // 等待1秒,取出PendingPost對象
                    PendingPost pendingPost = queue.poll(1000);
                    ...
                    // ->> 分析14(在上面)
                    eventBus.invokeSubscriber(pendingPost);
                }
            } catch (InterruptedException e) {
                eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e);
            }
        } finally {
            executorRunning = false;
        }
    }
}
複製代碼

總結一下

post也不難,首先是將發送的事件保存在postingState中的隊列裏面,它是線程獨有的,而後遍歷postingState中的事件隊列,拿出該線程下,全部的事件的集合,而後遍歷它,再根據subscriptionsByEventType,取出該事件所對應的全部訂閱方法,而後看是否可以直接處理,若是能,直接反射調用訂閱方法,若是不能,直接經過HandlerPower、BackgroundPower、AsyncPower切換線程後,再進行反射調用處理。

其中HandlerPower內部就直是封裝了個Handler,每次調用的時候,先將事件加入到隊列中,而後根據Handler切換到主線程,按順序取出隊列中的事件,反射執行 BackgroundPower是封裝了catchThreadPool用於執行任務, AsyncPower與它相似,可是裏面沒有同步鎖,每次執行都會新開闢一個子線程去執行任務。


5.Sticky粘性事件

什麼是粘性事件?通常來講,咱們使用 EventBus 都是先準備好訂閱事件的方法,而後註冊事件,最後在發送事件,即要先有事件的接收者。但粘性事件卻偏偏相反,咱們能夠先發送事件,後續再準備訂閱事件的方法、註冊事件。

先看下如何使用,其實很簡單

// 發佈事件
EventBus.getDefault().postSticky(new Object());

// 訂閱事件
@Subscribe(sticky = true)
public void testEventBus(Object obj){

    ...
}
複製代碼

事件都發送了,再註冊訂閱方法居然還能接收到以前的事件,它是怎麼作到的? 看圖

圖在下面

看代碼

public void postSticky(Object event) {

    // 很簡單,將要發佈的粘性事件的類型和對應事件,存入map stickyEvents中
    synchronized (stickyEvents) {
        stickyEvents.put(event.getClass(), event);
    }
    // 這個就是一個普通的發佈事件,上文分析過了
    post(event);
}
複製代碼

咱們將發佈的粘性事件的類型和對應事件存入了咱們的map中,那麼咱們是在哪裏執行的尼? 還記得咱們上面寫的分析5嗎?我把代碼補全下,在註冊方法中

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {

    // 構建那兩個很重要的map
    ...

    // 若是該事件支持粘性事件的話
    if (subscriberMethod.sticky) {

        // 若是須要向上查找事件的父類
        if (eventInheritance) {
                
            // 遍歷咱們上面存儲的粘性事件的集合,取出裏面存儲的粘性事件
            Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
            for (Map.Entry<Class<?>, Object> entry : entries) {
                Class<?> candidateEventType = entry.getKey();

                // 若是candidateEventType是eventType的子類
                if (eventType.isAssignableFrom(candidateEventType)) {
                    Object stickyEvent = entry.getValue();

                    // ->> 分析16
                    checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                }
            }
        } else {
            Object stickyEvent = stickyEvents.get(eventType);
            checkPostStickyEventToSubscription(newSubscription, stickyEvent);
        }
    }
}

/** * 分析16 */
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
        
    // 若是該方法支持粘性事件 
    if (stickyEvent != null) {
            
        // 上面分析過這個方法,根據線程模式,去處理事件
        postToSubscription(newSubscription, stickyEvent, isMainThread());
    }
}
複製代碼

看,是否是很簡單,來咱們總結下

總結一下

若是須要發送粘性事件的話,在發送的時候,會將粘性事件的事件類型和對應事件存儲到map stickyEvents中,等新的註冊類進行註冊的時候,若是有的訂閱方法支持粘性事件,則會在註冊的時候,取出stickyEvents裏面的存儲粘性事件,而後遍歷處理事件。

好了,eventbus就分析完畢了,下面一張大圖,來加深印象


6.看圖

在這裏插入圖片描述

7.最後想說的

其實這個eventbus在全部開源項目中,是屬於那種比較經典的,裏面設計的很巧妙,有興趣的小夥伴們能夠手動寫一個eventbus。另外尼,在這個再留個小思考

eventbus支不支持跨進程?爲何?知道的小夥伴能夠在下面留言


8.下集預告

讓咱們本身手動擼一個Butterknife

相關文章
相關標籤/搜索