EventBus代碼解讀

前言、提問
1.EventBus如何使用?
2.EventBus的執行流程?
3.onEvent****(Object o)與onEvent****(具體的類型)比較,哪個效率更高?各有什麼好處?
4.EventBus註冊查找等是在UI線程實現嗎?EventBus的消息處理如何實現在UI線程、後臺線程、異步線程裏運行?
5.post()和postStick()方法的區別?
6.EventBus中使用到哪些設計模式?
7.不調用EventBus.unregister()爲何會造成內存泄露?
8.onEvent系列方法在異步線程中執行是同一個線程還是多個線程?
9.EventBus中優先級的用處?
10.EventBus中用到的技術點

一、EventBus使用方法和執行流程
1.1 使用方法
      EventBus的使用包括收發消息。發送消息通過調用EventBus.getDefault().post()或postStick()方法。接收消息,需要在Subscriber(如Activity、Fragment)中的onCreate()中調用EventBus.getDefault().register(),在onDestroy()調用EvenBus.getDefault().unregister()方法,在onEvent系列方法中對消息進行處理,可以區分在UI線程或者非UI線程中處理。

1.2 執行流程
       EventBus.getDefault()首先通過單例模式創建或獲取全局唯一的對象。
       當調用register()時,會在參數類的文件內尋找onEvent系列方法。如果找不到就拋出異常;如果找到就將參數類和onEvent***方法、消息的類型(class)等信息以鍵值對的形式存儲在EventBus對象裏的HashMap中。當調用unregister()時,會將參數類對應的值刪除。如果只調用register(),未在onDestroy()中調用unregister(),會造成內存泄露。
       當調用post***()方法時,會根據發送的消息類型,找到能處理該類型消息的所有類對象和消息處理方法,並根據是否在UI線程進行執行。如果在UI線程,則通過Handler執行;如果在異步線程,則在EventBus中創建的線程池中執行。

二、代碼導讀
       閱讀代碼前,先記住幾個類或名詞。
       註冊EventBus的類稱爲Subscriber(register()和unregister()傳入的參數,實際處理消息的類)
       封裝消息的類是PendingPost
       消息的類型稱爲EventType,是消息類的class(類字面常量,onEvent***()方法傳入的形參是消息類的對象)
       封裝消息處理方法的類是SubscriberMethod
       封裝Subscriber、消息處理方法和優先級信息的類是Subscribtion
       
2.1  getDefault() 
       使用EventBus,首先要用到EventBus的getDefault()方法。
       getDefault()採用單例模式。首先判斷defaultInstance是否爲空,如果不爲空則直接返回;如果爲空,加同步鎖,保證在不同線程裏同時調用返回的是同一個對象。在同步塊中,仍然判斷是否爲空,防止在其他線程中已經創建了EventBus對象;如果爲空,則創建,並返回。
/** Convenience singleton for apps using a process-wide EventBus instance. */
public static EventBus getDefault() {
    if (defaultInstance == null) {
        synchronized (EventBus.class) {
            if (defaultInstance == null) {
               defaultInstance = new EventBus();
            }
        }
    }
    return defaultInstance;
}
      其中,defaultInstance變量是volatile修飾的EventBus對象句柄,保證在任一線程中發生更改,在其他線程中其值都是最新的。
static volatile EventBus defaultInstance;

2.2 EventBus的構造函數
       getDefault()創建EventBus的對象,會調用EventBus的無參構造函數。
/**
 * Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a
 * central bus, consider {@link #getDefault()}.
 */
public EventBus() {
    this(DEFAULT_BUILDER);
}
這裏要注意,使用單例模式來設計的類,其構造函數應該爲private。但這裏聲明爲public,破壞了單例的設計。(爲何要這樣?)
EventBus的構造函數使用了Builder模式。DEFAULT_BUILDER是EventBus內部聲明的static變量。
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
上面EventBus的公有構造函數調用了它的重載方法。
EventBus(EventBusBuilder builder) {
   subscriptionsByEventType = new HashMap<Class<?>, CopyOnWriteArrayList<Subscription>>();
   typesBySubscriber = new HashMap<Object, List<Class<?>>>();
   stickyEvents = new ConcurrentHashMap<Class<?>, Object>();
   mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
   backgroundPoster = new BackgroundPoster(this);
   asyncPoster = new AsyncPoster(this);
   subscriberMethodFinder = new SubscriberMethodFinder(builder.skipMethodVerificationForClasses);
   logSubscriberExceptions = builder.logSubscriberExceptions;
   logNoSubscriberMessages = builder.logNoSubscriberMessages;
   sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
   sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
   throwSubscriberException = builder.throwSubscriberException;
   eventInheritance = builder.eventInheritance;
   executorService = builder.executorService;
}

2.2.1 subscriptionsByEventType
        subscriptionsByEventType是存儲消息類型(EventType)和消息處理方法(onEvent***())等信息的數據結構。
        如Activity的activitya對象和activityb對象都有onEvent***(A a)方法,則subscriptionsByEventType存儲A.class(key)和[由activitya、onEvent***、priority等字段拼裝的Subscribtion, 由activityb、onEvent***、priority等字段拼裝Subscribtion](value)。
2.2.2 typesBySubscriber
       typesBySubscriber是存儲消息處理對象(Subscriber)和消息類型列表(EventType)的數據結構。如Activity的activitya對象有onEvent**(A a)和onEvent***(B b)兩個方法,則typesBySubscriber中會存儲activitya(key)和[A.class, B.class](value)。
typesBySubscriber的添加在register()中:
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
    subscribedEvents = new ArrayList<Class<?>>();
    typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
刪除在unregister()中:
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
typesBySubscriber.remove(subscriber);
因此如果在activitya對象的onDestroy中未調用unregister()方法,由於typesBySubsciber變量中仍包含activitya的對象句柄,會造成activitya無法被垃圾回收,從而造成內存泄露。
2.2.3  mainThreadPoster
      mainThreadPoster的類型是HandlerPoster,繼承於Handler,內有PendingPostQueue對象。其中,PendingPostQueue是一個自定義隊列(先進先出),有enqueue()和poll()方法。
      HandlerPoster有enqueue()和重寫於Handler的handleMessage()方法。其中,enqueue()方法將消息(PendingPost)添加到PendingPostQueue對象中,並通過Handler的sendMessage()方法,調用自身的handleMessage()方法。
      在handleMessage中,通過queue.poll()方法消息(PendingPost)取出,進行處理,處理完成後,如果處理時間超過單次最大處理時間,則通過sendMessage加入到UI線程的消息隊列中,結束本次handleMessage()的調用。如果未超過單次最大處理時間,則繼續通過poll()方法獲取下個消息(PendingPost),繼續處理,直到消息都處理完或者總處理時間超過單次最大處理時間。這種實現方式,既保證了UI線程不會被長時間佔用,又加快了消息的處理速度(從發出到處理完的時間儘量短)。
2.2.3.1 HanlerPoster的實現依賴Looper線程,不一定是UI線程,
HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
    super(looper);
    this.eventBus = eventBus;
    this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
    queue = new PendingPostQueue();
}
看下mainThreadPoster 的創建:
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
傳入的是Looper.getMainLooper(),因此消息的處理是在UI線程中完成的。
2.2.3.2 看下HandlerPoster的enqueue()方法:
void enqueue(Subscription subscription, Object event) {
    PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
    synchronized (this) {
        queue.enqueue(pendingPost);
        if (!handlerActive) {
            handlerActive = true;
            if (!sendMessage(obtainMessage())) {
                throw new EventBusException("Could not send handler message");
            }
        }
    }
}
其中PendingPost內部使用了對象池,避免對象反覆創建銷燬,有興趣的可以看下源碼。
2.2.3.3 這裏有個疑問,PendingPost和Message的實現基本一致,都是使用對象池,而且PendingPostQueue和MessageQueue的實現也一致,那麼爲何不直接使用Message和MessageQueue?
看下HandlerPoster的handlerMessage()方法
@Override
public void handleMessage(Message msg) {
    boolean rescheduled = false;
    try {
        long started = SystemClock.uptimeMillis(); //Returns milliseconds since boot, not counting time spent in deep sleep.
        while (true) {
            PendingPost pendingPost = queue.poll();
            if (pendingPost == null) {
                synchronized (this) {
                   // Check again, this time in synchronized
                    pendingPost = queue.poll();
                    if (pendingPost == null) {
                        handlerActive = false;
                        return;
                    }
                }
            }
            eventBus.invokeSubscriber(pendingPost);
            long timeInMethod = SystemClock.uptimeMillis() - started;
            if (timeInMethod >= maxMillisInsideHandleMessage) {
                if (!sendMessage(obtainMessage())) {
                     throw new EventBusException("Could not send handler message");
                }
                rescheduled = true;
                return;
            }
        }
    } finally {
        handlerActive = rescheduled;
    }
}
區別在於如果直接使用Message,單次handleMessage(),只執行一個subscription。
而使用PendingPost,單次handlerMessage(),可以執行0個(如果爲空)或一個或多個subscription;而且,當handlerActive的情況下,enqueue()操作不需要調用sendMessage()。
此處應注意// Check again, this time in synchronized,具體見2.2.4分析。

2.2.4 BackgroundPoster
       BackgroundPoster實現了Runnable接口,由EventBus的內置線程池(見2.2.7)運行。與HandlerPoster相同,enque()方法將PendingPost對象添加進隊列,不同的是BackgroundPoster是通過ExecutorService進行執行。
       在BackgroundPoster的run方法中,從隊列中取出pendingPost並執行,實現了在非UI線程中執行的意圖;同時,通過無限循環,會一直執行到隊列中沒有數據且wait(1000)後再取也沒有,纔會退出線程。這種實現方式與IntentService的實現思路一致。當有BackgroundThread執行時,會將工作交給正在執行的BackgroundThread,而不是再創建新的BackgroundThread,避免了線程的重複創建與銷燬;同時,如果一直沒有新的消息需要處理,爲了避免線程一直在後臺執行,浪費CPU和電量,會將線程停止。
@Override
public void run() {
    try {
        try {
            while (true) {
                PendingPost pendingPost = queue.poll(1000);
                if (pendingPost == null) {
                   synchronized (this) {
                        // Check again, this time in synchronized
                        pendingPost = queue.poll();
                        if (pendingPost == null) {
                             executorRunning = false;
                             return;
                        }
                    }
                }
                eventBus.invokeSubscriber(pendingPost);
            }
        } catch (InterruptedException e) {
            Log.w("Event", Thread.currentThread().getName() + " was interruppted", e);
        }
    } finally {
        executorRunning = false;
    }
}
這裏需要注意的是,繼承Thread的線程多次start()會運行錯誤,而implement Runnable 的線程多次start()卻不會運行出錯。
比較new Thread().start();與
Runnable r  = new Runnable();
new Thread(r).start();
new Thread(r).start();可以看到後者並不違背Thread.start()後不能再次start()的原則。
但是不能理解的是爲何需要checkAgain需要in synchronized?這裏需要看下enqueue()方法
public void enqueue(Subscription subscription, Object event) {
    PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
    synchronized (this) {
        queue.enqueue(pendingPost);
        if (!executorRunning) {
            executorRunning = true;
            eventBus.getExecutorService().execute(this);
        }
    }
}
       所以synchronized(this)針對的是executorRunning變量,由於enqueue()方法是在調用enqueue()方法的線程裏執行,而run()方法在工作線程中執行,所以對executorRunning變量更改需要做同步處理。至於將queue.enqueue()和queue.poll()方法也放進去,是因爲如果將其放在外面,那麼有可能出現先執行queue.poll(),爲null;再執行queue.enqueue(pendingPost);之後執行executorRunning爲false,線程停止,那麼這個pendingPost就未被處理導致遺漏。
      從以上對BackgroundPoster代碼的分析可以看出,onEventBackground()的處理,不一定是在不同的線程中,也不一定在相同的線程中。

2.2.5 AsyncPoster
        AsyncPoster與BackgroundPoster同樣實現了Runnable,但AsyncPoster中一個線程的創建與銷燬只伴隨一個消息的處理,保證每個消息的處理(onEventAsync())都是在不同的線程中執行。

2.2.6 ExecutorService
        executorService賦值爲builder的executorService 而EventBusBuilder中,
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;
是全局唯一的線程池。

2.2.7 SubscriberMethodFinder
        subscriberMethodFinder是在註冊EventBus的Subscriber(如Activity、Fragment)中查找消息處理方法(SubscriberMethod)的類,將查找到的方法存儲在其內部的methodCache中。
private static final Map<String, List<SubscriberMethod>> methodCache = new HashMap<String, List<SubscriberMethod>>();
其查找方法的函數爲findSuscriberMethods()
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    String key = subscriberClass.getName();
    List<SubscriberMethod> subscriberMethods;
    synchronized (methodCache) { 
        subscriberMethods = methodCache.get(key); //先從緩存中讀取
    }
    if (subscriberMethods != null) {
        return subscriberMethods; // 緩存中有,直接返回
    }
    subscriberMethods = new ArrayList<SubscriberMethod>();
    Class<?> clazz = subscriberClass;
    HashSet<String> eventTypesFound = new HashSet<String>();
    StringBuilder methodKeyBuilder = new StringBuilder();
    while (clazz != null) {
        String name = clazz.getName();
        if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {
             // Skip system classes, this just degrades performance
             break;
        }

        // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
        Method[] methods = clazz.getDeclaredMethods(); //獲取所有方法
        for (Method method : methods) {
            String methodName = method.getName();
            if (methodName.startsWith(ON_EVENT_METHOD_NAME)) { //private static final String ON_EVENT_METHOD_NAME = "onEvent";
                 int modifiers = method.getModifiers(); //獲取參數修飾
                 if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { //公有方法且非忽略方法
                    Class<?>[] parameterTypes = method.getParameterTypes();
                    if (parameterTypes.length == 1) { //只處理一種類型
                        String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length());
                        ThreadMode threadMode;
                        if (modifierString.length() == 0) { // 默認爲當前線程中執行
                            threadMode = ThreadMode.PostThread;
                        } else if (modifierString.equals("MainThread")) {//主線程中執行
                            threadMode = ThreadMode.MainThread;
                        } else if (modifierString.equals("BackgroundThread")) {//後臺線程中執行
                            threadMode = ThreadMode.BackgroundThread;
                        } else if (modifierString.equals("Async")) {//完全異步線程中執行
                            threadMode = ThreadMode.Async;
                        } else { // 其他種不處理
                            if (skipMethodVerificationForClasses.containsKey(clazz)) { 
                                continue;
                            } else {
                                throw new EventBusException("Illegal onEvent method, check for typos: " + method);
                            }
                        }
                        Class<?> eventType = parameterTypes[0]; //消息類型
                        methodKeyBuilder.setLength(0); //置空
                        methodKeyBuilder.append(methodName);//方法名
                        methodKeyBuilder.append('>').append(eventType.getName());//消息類型的包名+類名
                        String methodKey = methodKeyBuilder.toString();
                        if (eventTypesFound.add(methodKey)) {
                             // Only add if not already found in a sub class
                            subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));
                        }
                    }
                } else if (!skipMethodVerificationForClasses.containsKey(clazz)) {
                    Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + clazz + "."
+ methodName);
                }
            }
        }
        clazz = clazz.getSuperclass();
    }
    if (subscriberMethods.isEmpty()) {
         throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called "
+ ON_EVENT_METHOD_NAME);
    } else {
         synchronized (methodCache) {
              methodCache.put(key, subscriberMethods);
        }
        return subscriberMethods;
    }
}
       如果爲onEventMainThread(EventA a),則methodKey爲onEventMainThread>*.*.EventA,threadMode爲ThreadMode.MainThread,eventType爲*.*.EventA(EventA的包名+類名)。

2.2.8 stickyEvents 
        stickyEvents存儲消息的類型信息(class)和消息的對象,如postSticky(EventA  eventa),stickyEvents中會增加EventA.class(key)和eventa(value)的鍵值對。

2.2.9 eventInheritance
        eventInheritance表明是否開啓消息查找父類模式,默認爲true。舉個例子,存在EventA和EventB,其中EventB繼承於EventA,如果Activity activitya中有onEvent***(EventA eventa)方法。當調用post(EventB eventb)時,如果eventInheritance爲true,那麼activitya的onEvent***(eventA eventa)方法也會被執行;如果eventInheritance爲false,則不會。

2.2.10 eventTypesCache
private static final Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<Class<?>, List<Class<?>>>();
eventTypesCache是存儲消息類型字面量和消息及其父類的類型字面量列表的Map。拿String類來說,eventTypesCache存儲以String.class(key)和[String.class, Object.class]爲鍵值對的數據。
/** Looks up all Class objects including super classes and interfaces. Should also work for interfaces. */
private List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
    synchronized (eventTypesCache) {
        List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
        if (eventTypes == null) {
            eventTypes = new ArrayList<Class<?>>();
            Class<?> clazz = eventClass;
            while (clazz != null) {
                eventTypes.add(clazz);
                addInterfaces(eventTypes, clazz.getInterfaces());
                clazz = clazz.getSuperclass();
            }
            eventTypesCache.put(eventClass, eventTypes);
        }
        return eventTypes;
    }
}

2.3 register()和unregister()方法
2.3.1 register()方法
EventBus的register()方法調用了自己的重載方法,
public void register(Object subscriber) {
    register(subscriber, false, 0);
}
regsiter(subscriber, sticky, priority)
private synchronized void register(Object subscriber, boolean sticky, int priority) {
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
    for (SubscriberMethod subscriberMethod : subscriberMethods) {
        subscribe(subscriber, subscriberMethod, sticky, priority);
    }
}

2.3.1.1 findSubscriberMethods()
       首先通過subscriberMethodFinder通過類型信息獲取subscriber類中定義的onEvent***()方法列表,存儲在subscriverMethods中。具體見2.2.7。
2.3.1.2 subscribe()
       之後,調用subscribe方法
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
    Class<?> eventType = subscriberMethod.eventType;
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);//獲取註冊了消息類型(eventType)的註冊者
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);
    if (subscriptions == null) {//如果沒註冊該消息類型
        subscriptions = new CopyOnWriteArrayList<Subscription>();
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {// 如果已經註冊,此種情況爲,針對同一個Subscriber對象(如Activity activity、Fragment fragment等)中調用了兩次register()方法,無論是否是重載的register()方法
        if (subscriptions.contains(newSubscription)) {
            throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
        }
    }

    // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
    // subscriberMethod.method.setAccessible(true);
    int size = subscriptions.size();
    for (int i = 0; i <= size; i++) {// 按優先級插入,優先級高的在前面,優先級低的在後面
        if (i == size || newSubscription.priority > subscriptions.get(i).priority) {
            subscriptions.add(i, newSubscription);
            break;
        }
    }

    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);//按消息類型添加subscriber
    if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<Class<?>>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    subscribedEvents.add(eventType);

    if (sticky) { //??? what meaning
        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);
        }
    }
}

2.3.1.2.1 CopyOnWriteArrayList的add(int, E)方法
public synchronized void add(int index, E e) {
    Object[] newElements = new Object[elements.length + 1];
    System.arraycopy(elements, 0, newElements, 0, index);
    newElements[index] = e;
    System.arraycopy(elements, index, newElements, index + 1, elements.length - index);
    elements = newElements;
}
add(E)方法
public synchronized boolean add(E e) {
    Object[] newElements = new Object[elements.length + 1];
    System.arraycopy(elements, 0, newElements, 0, elements.length);
    newElements[elements.length] = e;
    elements = newElements;
    return true;
}
可以看到CopyOnWriteArrayList與ArrayList的區別,ArrayList是聲明一個定長數組,添加數據直接在數組裏添加,當數目超過數組長度時,再聲明一個更長的數組,將數據從舊數組copy到新數組裏。而CopyOnWriteArrayList是ArrayList的特例,它的數組總是被填滿的,每次添加數據時,都伴隨着新數組的創建,而且新數組長度比舊數組長度只多一,因此每次添加操作都伴隨着數據的複製。
2.3.1.2.2 sticky 參數
????
2.3.1.2.3 看下subscriptions.contains(newSubscription),其取決於ArrayList中的object是否equals給定的參數。Subscription類中重寫了equals()方法。
@Override
public boolean equals(Object other) {
    if (other instanceof Subscription) {
        Subscription otherSubscription = (Subscription) other;
        return subscriber == otherSubscription.subscriber && subscriberMethod.equals(otherSubscription.subscriberMethod);
    } else {
        return false;
    }
}
因此如果針對相同的subscriber對象調用register()兩次,無論是否爲重載的,都會走到
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);

2.3.2 unregister()方法
unregister()方法必須要調用,否則會造成內存泄露,具體原因見之前對typesBySubscriber的介紹。
/** 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) {
            unubscribeByEventType(subscriber, eventType);
        }
        typesBySubscriber.remove(subscriber);
    } else {
        Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
    }
}
     整體思路就是把不需要再接收消息的Subscriber對象和處理方法(subscription)從HashMap中清除掉。比較有意思的是unubscribeByEventType()裏for循環的寫法。
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
private void unubscribeByEventType(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--;
            }
        }
    }
}
       首先,一個subsciptions列表中可以有多個subscription的subscriber與傳入的subscriber相同,這是由於對相同的事件類型,可以有不同的threadMode,即一個Subscriber中,可以有onEventMainThread(EventA eventa),也可以有onEventBackgroundThread(EventA eventa)、onEventAsync(EventA eventa)、onEvent(EventA eventa)。
       其次,在對List做遍歷操作的同時更改List的內容,如果書寫不當,是會拋出Concurrent異常的。此處的寫法無問題,很值得借鑑。

2.4 post()與postSticky()
/** Posts the given event to the event bus. */
public void post(Object event) {
    PostingThreadState postingState = currentPostingThreadState.get(); //獲取當前線程中postMessage的狀態
    List<Object> eventQueue = postingState.eventQueue; //當前未完成的事件處理隊列
    eventQueue.add(event);

    if (!postingState.isPosting) { 
        postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
        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;
        }
    }
}

2.4.1  currentPostingThreadState是EventBus對象內部的ThreadLocal類型的變量,雖然是一個對象,但在不同的線程中調用get()方法,會返回該線程的PostingThreadState變量,每個線程的PostingThreadState都指向不同的對象句柄。
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
    @Override
    protected PostingThreadState initialValue() {
         return new PostingThreadState();
    }
};

2.4.1.1 這裏用到了java的匿名內部類,實際上是創建一個繼承於ThreadLocal類的對象,完整寫法爲:
class ThreadLocalSub<T> extends ThreadLocal {
        @Override
        protected PostingThreadState initialValue() {
            return new PostingThreadState();
        }
}
private final ThreadLocalSub<PostingThreadState> currentPostingThreadState = new ThreadLocalSub<PostingThreadState>();

2.4.1.2 看下ThreadLocal的get()方法
/**
 * Returns the value of this variable for the current thread. If an entry
 * doesn't yet exist for this variable on this thread, this method will
 * create an entry, populating the value with the result of
 * {@link #initialValue()}.
 *
 * @return the current value of the variable for the calling thread.
 */
@SuppressWarnings("unchecked")
public T get() {
    // Optimized for the fast path.
    Thread currentThread = Thread.currentThread();
    Values values = values(currentThread);
    if (values != null) {
        Object[] table = values.table;
        int index = hash & values.mask;
        if (this.reference == table[index]) {
            return (T) table[index + 1];
        }
    } else {
        values = initializeValues(currentThread);
    }

    return (T) values.getAfterMiss(this);
}
       其中,Thread對象內部存有Values對象, 如果Values爲空,則創建一個,並賦值給Thread的Values對象;如果不爲空,則在values.table中尋找當前ThreadLocal對象的值(Thread可以有多個ThreadLocal變量),如果找到,則直接返回;如果未找到,則創建,實際上調用的是ThreadLocal的initialValue方法(具體可見values.getAfterMiss(this)方法)。另外這是一種很好的解耦方式。

2.4.1.3 PostingThreadState類存儲有當前線程中EventBus發送消息的相關信息,如事件隊列、當前是否主線程、當前發送的消息、消息類型、是否被取消、是否在發送。
/** For ThreadLocal, much faster to set (and get multiple values). */
final static class PostingThreadState {
    final List<Object> eventQueue = new ArrayList<Object>();
    boolean isPosting;
    boolean isMainThread;
    Subscription subscription;
    Object event;
    boolean canceled;
}
      這裏需要提個問題:爲什麼需要使用ThreadLocal變量,而不是一個全局的PostingThreadState對象?
      答案是明顯的,如果使用一個全局的PostingThreadState變量,那麼多個線程中調用post()方法就必須進行同步操作,反覆獲取鎖、丟棄鎖,造成性能消耗;同時,post操作淪爲一個順序的操作,雖然可以多線程中調用,卻只能一個接一個的發送。而使用ThreadLocal變量,每個線程中自己進行狀態維護,可以並行執行,且不需要同步,性能消耗小。
      但另一個問題來了,由於使用了ThreadLocal變量,所以post()代碼只需要考慮單個線程執行的情況,那麼使用isPosting變量進行鎖限制,就很多餘了。因爲一個線程裏,必然要執行完前一個方法,才執行下個方法。這是爲什麼呢?

2.4.2 post()方法主要調用了postSingleEvent()方法
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) {
            Log.d(TAG, "No subscribers registered for event " + eventClass);
        }
        if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                eventClass != SubscriberExceptionEvent.class) {
            post(new NoSubscriberEvent(this, event));
        }
    }
}
postSingleEvent()方法又主要調用了postSingleEventForEventType()方法。
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;
}
這裏有個問題,如果subscriptions是ArrayList類型,對subscriptions的遍歷不在synchronized語句塊裏,多線程情況下,應該有一定的概率會出現問題。
但是需要注意的是:subscriptions是CopyOnWriteArrayList類型的,它在java.util.cocurrent包中,在一個線程中對其遍歷,在另外線程中,對其進行更改,是經過同步處理的。
最終調用的是postToSubsciption方法,結合之前說的mainThreadPoster、backgroundPoster、asyncPoster,很好理解。
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    switch (subscription.subscriberMethod.threadMode) {
        case PostThread:
            invokeSubscriber(subscription, event);
        break;
        case MainThread:
            if (isMainThread) {
                invokeSubscriber(subscription, event);
            } else {
                mainThreadPoster.enqueue(subscription, event);
            }
        break;
        case BackgroundThread:
            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);
    }
}
主要看下invokeSubscriber()方法,原理很簡單,使用的是反射。
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);
    }
}

三、 onEvent****(Object o)與onEvent****(具體的類型)比較,哪個效率更高?各有什麼好處?
onEvent***(Object o)這種寫法,在subscriptionsByEventType裏,以Object.class(key)和subscribers(value)爲鍵值對存儲。
在eventInheritance爲true時,post()任意EventType的消息,都會調用onEvent***(Object o)方法。而eventInheritance爲false時,只有post(Object o)時,纔會調用onEvent***(Object o)方法。與onEvent***(具體的類型)比較,自然是後者的效率高。EventBus的實現思路,其實就是通過類型信息來做事件區分的。

四、EventBus註冊查找等是在UI線程實現嗎?EventBus的消息處理如何實現在UI線程、後臺線程、異步線程裏運行?
EventBus註冊查找等在調用的線程裏實現,已經做了線程同步。
見2.2.3、2.2.4、2.2.5、2.2.6

五、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. This can be registerSticky(Object) or getStickyEvent(Class).

六、設計模式
1.單例模式 見EventBus.getDefault()
2.Builder模式 EventBusBuilder
3.模版模式


七、不調用unregister()爲何會造成內存泄露?
見2.2.2

八、onEvent系列方法中異步線程中是同一個線程還是多個線程?
見2.2.3、2.2.4、2.2.5、2.2.6

九、優先級的用處
先得到調用 見register()部分介紹

十、用得好的技術點
1.自定義消息隊列
2.線程池和Runnable
3.對象池
4.設計模式
5.ThreadLocal
6.一些數據結構
7....

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章