1. 程式人生 > >EventBus3.0的使用及原始碼分析

EventBus3.0的使用及原始碼分析

一、首先,EventBus 3.0的簡單使用:

1、首先在Activity的對應生命週期中進行註冊和解綁

@Override
protected void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

@Override
protected void onStop() {
    super.onStop();
    EventBus.getDefault().unregister(this);
}

2、傳送: 定義一個指定的事件,並且在需要的位置去傳送該事件:

事件

public class MessageEvent {
    public String message;

    public MessageEvent(String message) {
                 this.message = message;
         }
}

傳送:主執行緒傳送:

public void postMain(View view) {
    EventBus.getDefault().post(new MessageEvent("主執行緒傳送的事件"));
}

3、接收:接收事件的方法要用 @Subscribe 註解標註

@Subscribe
public void receiveMessageEvent(MessageEvent messageEvent) {
    mTvText.setText("接收到:  " + messageEvent.message + "  執行緒名稱: " +  Thread.currentThread().getName());
}

4、ThreadMode: 指定接收事件的執行執行緒

EventBus通過列舉指定了五個執行緒模式:
- POSTING:訂閱事件的執行緒 與釋出事件的執行緒一樣。
- MAIN:訂閱事件會在主執行緒中執行
- MAIN_ORDERED

:訂閱事件會在主執行緒執行。如果您在一個帶有主執行緒的事件處理程式中釋出另一個事件,第一個事件處理程式將完成,然後在稍後的時間點呼叫第二個事件處理程式(當主執行緒有容量時)
- BACKGROUND:如果釋出執行緒不是主執行緒,則訂閱事件會直接在該執行緒被呼叫,如果釋出執行緒時在主執行緒,則使用一個獨立的執行緒去處理訂閱事件。
- ASYNC:訂閱事件的處理在一個獨立的執行緒中執行,總是獨立於釋出事件的執行緒和主執行緒

例如。你想讓接收事件的處理在主執行緒中執行:

@Subscribe(threadMode = ThreadMode.MAIN)
public void receiveMessageEvent(MessageEvent messageEvent) {
    mTvText.setText("接收到:  " + messageEvent.message + "  執行緒名稱: " + Thread.currentThread().getName());
}

5、priority:優先順序

說明: 為int型別, 預設priority為0。這個是相對同一個事件,不同訂閱者接收該事件的優先順序。優先順序越高的訂閱者越先接收到事件。

 @Subscribe(priority = 3)
    public void receiveMessageEvent(final MessageEvent messageEvent) {
            Log.d("===", "MainActivity接收到:  " + messageEvent.message + "  執行緒名稱: " + Thread.currentThread().getName());
    }

6、Sticky: 粘性事件

說明: 假如你想先發送了事件,但我等到需要時在接收,這是就需要用到Sticky。首先你需要通過postSticky來發送事件,然後你再在需要接收事件時註冊EventBus,然後處理事件的方法要在註解中使用sticky = true。
例如:

public class StickyActivity extends AppCompatActivity {

    private TextView mTvText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sticky);
        mTvText = findViewById(R.id.tv_text);

        //傳送粘性事件: 注意使用的是posoSticky方法
        EventBus.getDefault().postSticky(new MessageEvent("粘性事件"));

    }


    //點選在註冊,在傳送事件之後
    public void resister(View view) {
        EventBus.getDefault().register(this);

    }


    //需要在註解中設定sticy = true
    @Subscribe(sticky = true)
    public void receiveEvent(MessageEvent messageEvent) {
        mTvText.setText(messageEvent.message);
        Log.d("===", "StickyActivity接收到:  " + messageEvent.message + "  執行緒名稱: " + Thread.currentThread().getName());

    }

@Override
protected void onStop() {
    super.onStop();
    //注意這裡比普通的事件多了一步,就是需要把所有的粘性事件remove掉
    EventBus.getDefault().removeAllStickyEvents();
    EventBus.getDefault().unregister(this);

}


    public class MessageEvent {

        String message;

        public MessageEvent(String message) {
            this.message = message;
        }
    }

}

二、開始看EventBus的訂閱的原始碼:

1、首先看EventBus的初始化操作 EventBus.getDefault():

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

可以看到EventBus的生成使用了雙重鎖的懶漢單例模式,保證全域性只有一個EventBus例項,整個專案的事件傳送和訂閱都有改例項去管理。
這裡注意下EventBus裡面的幾個重要引數,下面會用到:

private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;   //根絕事件型別儲存的訂閱資訊Subscription(屬性包括訂閱者和訂閱方法) 的map
private final Map<Object, List<Class<?>>> typesBySubscriber; //根據訂閱者進行儲存的所有訂閱方法的map
private final Map<Class<?>, Object> stickyEvents;//所有的粘性事件

2、然後我們再來分析register方法

public void register(Object subscriber) {
    Class<?> subscriberClass = subscriber.getClass();

   //通過這個subscriberMethodFinder去找到訂閱者(例子中的Activity)所有的訂閱方法(用Subscribe註解標明的方法)
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}

可以看到通過 subscriber.getClass()拿到訂閱者的Class物件,然後利用subscriberMethodFinder去拿到訂閱者的所有訂閱方法(用Subscribe註解標明的方法),然後進行遍歷,呼叫 subscribe(subscriber, subscriberMethod)。

首先我們看下這個SubscriberMethod 是什麼東東?

public class SubscriberMethod {
    final Method method;//訂閱的方法
    final ThreadMode threadMode;//制度的執行緒模式
    final Class<?> eventType;//訂閱的事件
    final int priority;//訂閱的優先順序
    final boolean sticky;//是否為粘性
    /** Used for efficient comparison */
    String methodString;//訂閱方法的名稱

     ... ...
    }

好了,可以看到這個SubscriberMethod 其實就是儲存訂閱方法的一些屬性的物件。

3、好了,可以看到這個SubscriberMethod 其實就是儲存訂閱方法的一些屬性的物件。

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    //通過快取去拿到訂閱者的所有訂閱方法,注意看 METHOD_CACHE 為Map<Class<?>, List<SubscriberMethod>> 型別
    List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
    if (subscriberMethods != null) {
        return subscriberMethods;
    }
    //是否忽略註解器生成的MyEventBusIndex類
    if (ignoreGeneratedIndex) {
    //利用反射去獲取訂閱者的訂閱方法
        subscriberMethods = findUsingReflection(subscriberClass);
    } else {
    //從註解器生成的MyEventBusIndex類中獲得訂閱類的訂閱方法資訊
        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;
    }
}

從上面看到這裡利用了快取機制,儲存這個每個訂閱者對應的訂閱方法。
快取為空時,判斷是從註解器生成的類去獲取還是通過反射去獲取。眾所周知,利用反射使比較耗效能的,所以EventBus 3.0 之後添加了利用註解器去獲取(註解處理器是在編譯時掃描和編譯和處理註解,而反射是在執行時,這裡就可以知道為什麼註解器的比反射效率高了吧。
關於註解處理器的詳細知識可以參考這篇博文,裡面寫的非常祥細:Java註解處理器

4、我們這裡先看通過反射去獲取訂閱方法 findUsingReflection(subscriberClass) 的程式碼:

private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
    //獲取FindState
    FindState findState = prepareFindState();
    //把訂閱者和FindState關聯起來
    findState.initForSubscriber(subscriberClass);
    while (findState.clazz != null) {
        findUsingReflectionInSingleClass(findState);
        findState.moveToSuperclass();
    }
    return getMethodsAndRelease(findState);
}

然後我們來看看這個FindState是什麼鬼?

static class FindState {
    // 所有的訂閱方法
    final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
     //根據每種事件儲存對應的訂閱方法的Map
    final Map<Class, Object> anyMethodByEventType = new HashMap<>();
     //根據訂閱方法的名字儲存的訂閱者的Map
    final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
    final StringBuilder methodKeyBuilder = new StringBuilder(128);

    Class<?> subscriberClass;//訂閱者的Class
    Class<?> clazz; //訂閱者的Class
    boolean skipSuperClasses; 
    SubscriberInfo subscriberInfo;

    void initForSubscriber(Class<?> subscriberClass) {
        this.subscriberClass = clazz = subscriberClass;
        skipSuperClasses = false;
        subscriberInfo = null;
    }

。。。
}

可以看到這個FindState就是一個儲存訂閱者和訂閱方法資訊的實體類,包括了訂閱類的訂閱的所有事件型別和訂閱方法。

好了再回到上面那裡拿到findstate後,執行findUsingReflectionInSingleClass(findState)這個方法:

private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
        // This is faster than getMethods, especially when subscribers are fat classes like Activities
        //拿到訂閱者的所有方法
        methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {
         //拿到訂閱者的所有方法
        // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
        methods = findState.clazz.getMethods();
        findState.skipSuperClasses = true;
    }
    //遍歷所有的方法
    for (Method method : methods) {
        int modifiers = method.getModifiers();
        //篩選拿到使用public公有修飾符修飾的
        if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
            //獲取這個方法的引數型別
            Class<?>[] parameterTypes = method.getParameterTypes();
            if (parameterTypes.length == 1) {
                //獲取這個方法Subscribe.class型別的註解物件
                Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                if (subscribeAnnotation != null) {
                    //如果Subscribe.class型別的註解物件不為空則拿到它的第一個引數(其實就是傳送的事件),然後把這個事件,執行緒模式,優先順序,是否為粘性都儲存到這個的findState.subscriberMethods中
                    Class<?> eventType = parameterTypes[0];
                    if (findState.checkAdd(method, eventType)) {
                        ThreadMode threadMode = subscribeAnnotation.threadMode();
                        findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                    }
                }
            } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                throw new EventBusException("@Subscribe method " + methodName +
                        "must have exactly 1 parameter but has " + parameterTypes.length);
            }
        } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
            String methodName = method.getDeclaringClass().getName() + "." + method.getName();
            throw new EventBusException(methodName +
                    " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
        }
    }
}

這個方法的作用就是拿到訂閱者的所有方法的集合,遍歷這個集合,判斷這些方法是否滿足用@Subscribe標註,並且方法的引數長度為1,並且為公開的。那麼這個方法即為訂閱方法。

至此,這個 findUsingReflection(subscriberClass)的作用基本理清了。

5、好了,上面的分析都是在獲取這個訂閱者的所有訂閱方法。然後我們回到步驟二中,遍歷這個訂閱方法的集合,呼叫 subscribe(subscriber, subscriberMethod);

我們再看看裡面做了什麼?

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    //獲取事件型別
    Class<?> eventType = subscriberMethod.eventType;
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    //通過subscriptionsByEventType(步驟一有說:根據事件型別儲存所有的訂閱資訊)獲取訂閱資訊Subscription的集合
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    if (subscriptions == null) {
        subscriptions = new CopyOnWriteArrayList<>();
        //拿到當前事件對應的subscription集合
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {
        if (subscriptions.contains(newSubscription)) {
            throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                    + eventType);
        }
    }

    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;
        }
    }
    //拿到當前訂閱者的所有訂閱方法集合,並且把當前訂閱方法新增到訂閱集合中
    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
    if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    subscribedEvents.add(eventType);

    //判斷當前訂閱方法是否為粘性,如果是立刻把事件post給訂閱者
    if (subscriberMethod.sticky) {
        if (eventInheritance) {
            // Existing sticky events of all subclasses of eventType have to be considered.
            // Note: Iterating over all events may be inefficient with lots of sticky events,
            // thus data structure should be changed to allow a more efficient lookup
            // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
            Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
            for (Map.Entry<Class<?>, Object> entry : entries) {
                Class<?> candidateEventType = entry.getKey();
                if (eventType.isAssignableFrom(candidateEventType)) {
                    Object stickyEvent = entry.getValue();
                    checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                }
            }
        } else {
            Object stickyEvent = stickyEvents.get(eventType);
            checkPostStickyEventToSubscription(newSubscription, stickyEvent);
        }
    }
}

在此,EventBus的註冊到處就完成了。我們在回頭捋一遍:

先來回顧下這幾個成員變數:

Subscription : 訂閱資訊實體類。 屬性包括訂閱者和訂閱方法  

Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType: key為事件型別,value為訂閱者集合。就是儲存每個事件對應的訂閱者的集合的Map。
Map<Object, List<Class<?>>> typesBySubscriber: key為訂閱者,value為訂閱事件。就是儲存每個訂閱者所對應的訂閱方法集合。
Map<Class<?>, Object> stickyEvents: 粘性事件集合
  1. 首先拿到EventBus的例項,在註冊方法中拿到訂閱的物件,
  2. 再通過反射或者註解器拿到這個物件的所有訂閱方法。遍歷這個訂閱方法集合。
  3. 把當前的訂閱資訊(訂閱者和訂閱方法)儲存到subscriptionsByEventType 中
  4. 把當前的方法的訂閱事件儲存到typesBySubscriber,
  5. 還判斷當前的訂閱方法是否為粘性,如果是則post給訂閱者。
  6. 迴圈c、d、e直接結束。

這裡寫圖片描述

三、現在看下EventBus的傳送事件的原始碼。

1、post事件

public void post(Object event) {
    //獲取當前執行緒的postingState
    PostingThreadState postingState = currentPostingThreadState.get();
    //拿到當前現成的的事件佇列
    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()) {
                postSingleEvent(eventQueue.remove(0), postingState);
            }
        } finally {
            //分發完成改變當前執行緒的postingState狀態
            postingState.isPosting = false;
            postingState.isMainThread = false;
        }
    }
}

在這裡相信會對這個currentPostingState產生疑問,它是EventBus的 一個成員變數,但這個是什麼東西吶,有什麼作用吶?

private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
    @Override
    protected PostingThreadState initialValue() {
        return new PostingThreadState();
    }
};

ThreadLocal 叫執行緒區域性變數。它的作用就是把當前執行緒的某些狀態(如例子中的PostingState)儲存起來,避免在多執行緒中其他執行緒對這些狀態的影響。這裡不做詳細的解析,想更清楚的瞭解,ThreadLocal詳解

所以這個步驟的作用是拿到當前執行緒儲存的分發佇列進行分發。

2、好了。回到1中開始分發的while迴圈,看到事件的分發是通過postSingleEvent(eventQueue.remove(0), postingState)執行。

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    Class<?> eventClass = event.getClass();
    boolean subscriptionFound = false;
    //是否觸發當前事件所繼承的父類和介面
    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) {
            logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
        }
        if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                eventClass != SubscriberExceptionEvent.class) {
            post(new NoSubscriberEvent(this, event));
        }
    }
}

從上面的程式碼可以看到這裡的作用找出當前事件的父類和介面,並且新增到一個集合中,迴圈這個集合開始分發。

這裡有人會問為什麼會觸發父類和介面吶?

如果認真向下就會知道,加入A繼承B,那麼A是包括了B的。假如發出的事件時A,那麼自然就觸發了訂閱B事件的響應函式。當然,是否觸發父類和介面是可以設定的,這個需要通過EventBus的builder來設定。

3、好了,從2中看到方法會執行到postSingleEventForEventType(event, postingState, clazz) 這裡。

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
        //通過事件型別或者訂閱該事件的訂閱資訊集合
        subscriptions = subscriptionsByEventType.get(eventClass);
    }
    if (subscriptions != null && !subscriptions.isEmpty()) {
        //向每個訂閱者分發事件
        for (Subscription subscription : subscriptions) {
            postingState.event = event;
            postingState.subscription = subscription;
            boolean aborted = false;
            try {
                //把事件分發訂閱者
                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有點眼熟。沒錯,這個就是註冊時儲存的根據每種訂閱事件型別儲存的訂閱者集合。拿到訂閱該事件的訂閱者集合,然後遍歷進行事件分發。

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    //在指定的執行緒進行響應
    switch (subscription.subscriberMethod.threadMode) {
        case POSTING://在當前執行緒中響應
            invokeSubscriber(subscription, event);
            break;
        case MAIN://在主執行緒中響應
            if (isMainThread) {
                invokeSubscriber(subscription, event);
            } else {
                mainThreadPoster.enqueue(subscription, event);
            }
            break;
        case MAIN_ORDERED:
            if (mainThreadPoster != null) {
                mainThreadPoster.enqueue(subscription, event);
            } else {
                // temporary: technically not correct as poster not decoupled from subscriber
                invokeSubscriber(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);
    }
}

明顯,這裡是做一個執行緒切換的判斷。
而切換執行緒時通過poster來進行切換,從程式碼中可以看到有mainThreadPoster,backgroundPoster,asyncPoster。mainThreadPoster是在訂閱初始化時就建立的,而backgroundPoster,asyncPoster內部都通過EventBus的getExecutorService(),而獲取到的就是 Executors.newCachedThreadPool()。這個是原生的四種執行緒池的其中一個。

4、從3可以看到接收到訂閱事件的響應函式是在 invokeSubscriber(subscription, event)中執行的。

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);
    }
}

至此,EventBus的post到此完成。

好了,我們在來捋一下整個流程。
1. 通過ThreadLocal拿到當前執行緒儲存的事件分發佇列,並把當前事件新增到佇列中;
1. 如果觸發事件的父類和介面,則需要拿到它們並新增到一個事件集合中;
1. 遍歷事件集合,然後通過subscriptionsByEventType拿到訂閱該事件的訂閱者集合,遍歷該訂閱者集合 在相應的執行緒中通過反射進行響應函式的呼叫執行。

這裡寫圖片描述

四、再來看看解綁unRegister的原始碼。

public synchronized void unregister(Object subscriber) {
     //拿到當前訂閱者的訂閱事件集合
    List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
    if (subscribedTypes != null) {
        for (Class<?> eventType : subscribedTypes) {
              unsubscribeByEventType(subscriber, eventType);
        }
        // 把當前訂閱者的訂閱事件集合從總事件map中刪除 
        typesBySubscriber.remove(subscriber);
    } else {
        logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
    }
}

上面拿到訂閱者對於的訂閱事件集合後,進行遍歷,執行了 unsubscribeByEventType(subscriber, eventType)這個方法。

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--;
            }
        }
    }
}

上面的程式碼都不難,都是把訂閱者和訂閱事件從儲存的map中移除,避免了對這些將被銷燬的物件的引用,防止造成記憶體洩漏。

五、最後,我們來總結一下整個流程:

訂閱 register:
1. 在register訂閱方法拿到訂閱者,通過反射或者註解器拿到訂閱者的所有訂閱方法,進行遍歷。
1. 拿到這方法的訂閱資訊,根據優先順序插入到佇列中,再把佇列儲存到以事件為key的map中。
1. 把當前訂閱事件儲存到 以訂閱者為key的,訂閱事件集合為value的map中。
1. 判斷是否為粘性事件,是的話立刻把事件分發給訂閱者。
1. 遍歷是否完成,是即結束,否就重複步驟2。

傳送事件 post:
1. 通過ThreadLocal拿到當前執行緒的傳送佇列。把事件新增到佇列中;
1. 判斷事件是否正在分發,是則結束,否則繼續;
1. 遍歷事件佇列;
1. 判斷是否觸發父類和介面,是的話拿到該事件及它的繼承鏈父類,介面的集合,然後進行遍歷;
1. 從儲存所有訂閱資訊的map拿到以當前事件型別為key的訂閱資訊(訂閱者和訂閱方法);
1. 根絕訂閱方法指定的執行緒模式做執行緒切換,並通過反射執行訂閱方法。
1. 判斷兩個迴圈是否結束,是則結束,否則繼續。

取消訂閱 unregister:

  1. 拿到當前訂閱者的訂閱事件集合,遍歷;
  2. 從儲存所有訂閱資訊的map中移除以該訂閱事件為key的訂閱資訊集合;
  3. 迴圈是否結束。是則把當前當前訂閱者的訂閱事件集合從儲存的map中移除。否則繼續2步;
  4. 結束。