轉載:一文徹底搞懂EventBus 3.0原理

一文徹底搞懂EventBus 3.0原理

1.簡介

EventBus是一種用于Android的事件發(fā)布-訂閱總線框架,由GreenRobot開發(fā),Gihub地址是:EventBus。它簡化了應用程序內(nèi)各個組件之間進行通信的復雜度,尤其是碎片之間進行通信的問題,可以避免由于使用廣播通信而帶來的諸多不便。

2.Subscribe注解

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

@Subscribe
public void testEventBus(Object obj){
    ...
}
復制代碼

看下注解

@Documented
@Retention(RetentionPolicy.RUNTIME) // 注解不僅被保存到class文件中,jvm加載class文件之后,仍然存在
@Target({ElementType.METHOD})  // 作用在方法上
public @interface Subscribe {

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

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

    // 優(yōu)先級,如果指定了優(yōu)先級,則若干方法接收同一事件時,優(yōu)先級高的方法會先接收到。
    int priority() default 0;
}
復制代碼

ThreadMode可以指定的模式有:

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

3.register注冊

好了,要想使用Eventbus,則要先注冊它,看看如何使用

EventBus.getDefault().register(this);
復制代碼

很簡單吧,getDefault()其實就是一個單例模式,創(chuàng)建EventBus實例對象,并返回

public static EventBus getDefault() {

    if (defaultInstance == null) {
         synchronized (EventBus.class) {
             if (defaultInstance == null) {
                 defaultInstance = new EventBus();
             }
         }
    }
    return defaultInstance;
}
復制代碼

沒啥可說的,繼續(xù)看register

先看圖


圖片描述

再看代碼

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

     synchronized (this) {

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

從上面的圖可以看出,這個方法其實就是做了2件事

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

    // METHOD_CACHE: 是一個ConcurrentHashMap,key是要注冊類的字節(jié)碼文件,value是這個字節(jié)碼文件里的所有訂閱方法信息的集合,集合的元素是SubscriberMethod,它實際上就是訂閱方法的信息類,包含Method對象、線程模式、事件類型、優(yōu)先級、是否是粘性事等。
    List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass); // 這步實際上就是看看這個注冊類的方法是否已經(jīng)緩存了,緩存過就直接根據(jù)類返回
    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()方法中
    }
}
復制代碼

這個方法主要作用就是根據(jù)傳入的注冊類返回該類上所有的訂閱方法的信息,先找緩存METHOD_CACHE,有就走緩存,沒有就調(diào)用findUsingInfo方法獲取訂閱方法信息集合,然后再根據(jù)注冊類為key, 訂閱方法的信息集合為value, 存入緩存(METHOD_CACHE)中。

/**
 * 分析2:findUsingInfo()
 * 作用:如果findState緩存了,訂閱方法信息,則使用findState里的緩存,否則調(diào)用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個參數(shù)
            if (parameterTypes.length == 1) {

                // 獲取方法上的名為Subscribe的注解
                Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);

                // 如果該方法帶Subscribe注解
                if (subscribeAnnotation != null) {

                    // 獲取該訂閱方法上的第一個參數(shù)類型,也就是訂閱的事件類型
                    Class<?> eventType = parameterTypes[0];

                    // checkAdd()方法用來判斷FindState中是否已經(jīng)添加過將該事件類型為key的鍵值對,沒添加過則返回true
                    if (findState.checkAdd(method, eventType)) {

                        // 獲取線程模式
                        ThreadMode threadMode = subscribeAnnotation.threadMode();

                        // 將該訂閱方法,事件類型,線程模式,優(yōu)先級,是否支持粘性事件等信息,封裝成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)) {
            ...
        }
    }
}
復制代碼

根據(jù)反射,獲取訂閱方法的信息數(shù)據(jù),然后將它分封裝成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) {

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

    // 添加上邊創(chuàng)建的newSubscription對象到subscriptions中
    int size = subscriptions.size();
    for (int i = 0; i <= size; i++) {

        // 根據(jù)優(yōu)先級進行排序
        if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
            subscriptions.add(i, newSubscription);
            break;
        }
    }

    // typesBySubscriber也是一個HashMap,保存了以當前要注冊類的對象為key,注冊類中訂閱事件的方法的參數(shù)類型的集合為value的鍵值對
    // 和上面一樣,根據(jù)key先判斷,是否已經(jīng)存儲過了,如果已經(jīng)存儲過了,直接取出訂注冊類中訂閱事件的方法的參數(shù)類型的集合
    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構建完畢了,我們的注冊也就完事了

總結一下

傳入注冊類信息,根據(jù)反射獲取注冊類上的所有方法,遍歷這些方法,取出其中的訂閱方法(條件是,一個參數(shù),權限為public,使用了Subscribe標簽)將方法的信息封裝成SubscriberMethod對象,并存入集合,然后再遍歷這個集合,取出其中的SubscriberMethod對象,再根據(jù)注冊類的字節(jié)碼文件,合并成Subscription對象,再根據(jù)event類型,進行重新分類,存入map subscriptionsByEventType中(key 為event, value 為List),再創(chuàng)建map typesBySubscriber, 注冊類為key , list為value。 完事了。

4.unregister取消注冊

使用很簡單

EventBus.getDefault().unregister(this);
復制代碼

看圖

[圖片上傳中...(image-2118a1-1572414428651-3)]

看下代碼

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:還記得我們分析注冊時,創(chuàng)建的那2個map嗎? 其中一個是typesBySubscriber,key是注冊類,value是事件類型的集合(List), 這一步就是根據(jù)注冊類獲取該類所有訂閱方法的事件類型。

/**
 * 分析7
 */
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {

    // 根據(jù)事件類型,獲取該事件類型所對應的訂閱方法信息的集合
    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--;
             }
        }
    }
}
復制代碼

總結一下

解除綁定,其實比較簡單,主要就是運用注冊時所產(chǎn)生的2個map, 先根據(jù)typesBySubscriber,也就是根據(jù)要解除綁定的注冊類,找到這個類所擁有的所有訂閱事件,然后遍歷這些訂閱事件,再根據(jù)這些訂閱事件,在subscriptionsByEventType中找到,這個事件所對應的訂閱方法的集合,再遍歷集合,判斷該訂閱方法的注冊類信息,是否是要解除綁定的注冊類,如果是,移除該訂閱方法信息,完成解除綁定。

4.post發(fā)布事件

使用也很簡單

EventBus.getDefault().post(new Object());
復制代碼

看圖

[圖片上傳中...(image-c9216f-1572414428651-2)]

看代碼

public void post(Object event) {

    // ->> 分析8
    PostingThreadState postingState = currentPostingThreadState.get();

    // 獲取postingState里面存的一個隊列
    List<Object> eventQueue = postingState.eventQueue;

    // 將要發(fā)送的事件,存入隊列中
    eventQueue.add(event);

    // 判斷該事件是否正在發(fā)送,如果在發(fā)送,則跳過下面的邏輯
    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 {

            // 重置狀態(tài)
            postingState.isPosting = false;
            postingState.isMainThread = false;
        }
    }
}
復制代碼

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

post() 方法主要就是要先將發(fā)送的事件保存在postingState中的隊列里面,它是線程獨有的,然后通過循環(huán)隊列,將事件交給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);
        }

        // 如果我們沒有訂閱事件,則發(fā)送NoSubscriberEvent
        if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class && eventClass != SubscriberExceptionEvent.class) {
            post(new NoSubscriberEvent(this, event));
        }
    }
}
復制代碼

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

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

    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {

        // 還記得注冊時構建的map subscriptionsByEventType嗎?對,這步就是根據(jù)事件類型,獲取它所對應的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;
}
復制代碼

這個方法其實很簡單,就是根據(jù)事件類型,在subscriptionsByEventType中找到對應的訂閱方法信息的集合,然后遍歷集合,拿到訂閱方法信息的封裝類,調(diào)用postToSubscription去執(zhí)行。

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

    // 根據(jù)訂閱方法設置的線程模式去執(zhí)行
    switch (subscription.subscriberMethod.threadMode) {

        // 默認線程模式,在哪個線程發(fā)送事件,就在哪個線程接收事件
        case POSTING:

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

        // 如果是主線程,則直接執(zhí)行,子線程加入隊列,然后通過 Handler 切換到主線程執(zhí)行
        case MAIN:
            if (isMainThread) {

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

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

        // 無論在哪個線程,都加隊列,通過handler 在主線程執(zhí)行
        case MAIN_ORDERED:

            // ->> 分析13
            if (mainThreadPoster != null) {
                mainThreadPoster.enqueue(subscription, event);
            } else {
                invokeSubscriber(subscription, event);
            }
            break;

        // 如果在子線程中,直接執(zhí)行,如果在主線程中,加入隊列,通過線程池執(zhí)行
        case BACKGROUND:

            if (isMainThread) {

                // ->> 分析15
                backgroundPoster.enqueue(subscription, event);
            } else {

                // 在子線程,直接反射執(zhí)行
                invokeSubscriber(subscription, event);
            }
            break;

        // 無論在哪個線程執(zhí)行,都加入隊列,用線程池執(zhí)行
        case ASYNC:

             // AsyncPoster和backgroundPoster類型,但是AsyncPoster沒有加同步鎖,這也就造成了,它每次執(zhí)行一個任務,都會開一個子線程,而backgroundPoster不會
             asyncPoster.enqueue(subscription, event);
             break;
        default:
             throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
    }
}

/**
 * 分析12:直接通過反射調(diào)用執(zhí)行
 */
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()發(fā)送處理事件的消息,handleMessage()方法將被執(zhí)行,將子線程切換到主線程
                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) {

        // 通過反射調(diào)用執(zhí)行該訂閱方法
        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;

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

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

                // 循環(huán)隊列
                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也不難,首先是將發(fā)送的事件保存在postingState中的隊列里面,它是線程獨有的,然后遍歷postingState中的事件隊列,拿出該線程下,所有的事件的集合,然后遍歷它,再根據(jù)subscriptionsByEventType,取出該事件所對應的所有訂閱方法,然后看是否能夠直接處理,如果能,直接反射調(diào)用訂閱方法,如果不能,直接通過HandlerPower、BackgroundPower、AsyncPower切換線程后,再進行反射調(diào)用處理。

其中HandlerPower內(nèi)部就直是封裝了個Handler,每次調(diào)用的時候,先將事件加入到隊列中,然后根據(jù)Handler切換到主線程,按順序取出隊列中的事件,反射執(zhí)行 BackgroundPower是封裝了catchThreadPool用于執(zhí)行任務, AsyncPower與它類似,但是里面沒有同步鎖,每次執(zhí)行都會新開辟一個子線程去執(zhí)行任務。

5.Sticky粘性事件

什么是粘性事件?一般來說,我們使用 EventBus 都是先準備好訂閱事件的方法,然后注冊事件,最后在發(fā)送事件,即要先有事件的接收者。但粘性事件卻恰恰相反,我們可以先發(fā)送事件,后續(xù)再準備訂閱事件的方法、注冊事件。

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

// 發(fā)布事件
EventBus.getDefault().postSticky(new Object());

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

    ...
}
復制代碼

事件都發(fā)送了,再注冊訂閱方法竟然還能接收到之前的事件,它是怎么做到的? 看圖

圖在下面

看代碼

public void postSticky(Object event) {

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

我們將發(fā)布的粘性事件的類型和對應事件存入了我們的map中,那么我們是在哪里執(zhí)行的尼? 還記得我們上面寫的分析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) {

        // 上面分析過這個方法,根據(jù)線程模式,去處理事件
        postToSubscription(newSubscription, stickyEvent, isMainThread());
    }
}
復制代碼

看,是不是很簡單,來我們總結下

總結一下

如果需要發(fā)送粘性事件的話,在發(fā)送的時候,會將粘性事件的事件類型和對應事件存儲到map stickyEvents中,等新的注冊類進行注冊的時候,如果有的訂閱方法支持粘性事件,則會在注冊的時候,取出stickyEvents里面的存儲粘性事件,然后遍歷處理事件。

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

6.看圖

[圖片上傳失敗...(image-b8122c-1572415302007)]

7.最后想說的

其實這個eventbus在所有開源項目中,是屬于那種比較經(jīng)典的,里面設計的很巧妙,有興趣的小伙伴們可以手動寫一個eventbus。另外尼,在這個再留個小思考

eventbus支不支持跨進程?為什么?知道的小伙伴可以在下面留言

其他鏈接

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容