首頁>技術>

一、簡單示例1、首先,定義要傳遞的事件實體

public class CollectEvent { ... }複製程式碼
2、準備訂閱者:宣告並註解你的訂閱方法
@Subscribe(threadMode = ThreadMode.MAIN)public void onMessageEvent(CollectEvent event) {    LogHelper.d("OK");}複製程式碼
3、在2中,也就是訂閱中所在的類中,註冊和解註冊你的訂閱者
@Overridepublic void onStart() {    super.onStart();    EventBus.getDefault().register(this);}@Overridepublic void onStop() {    super.onStop();    EventBus.getDefault().unregister(this);}複製程式碼
4、傳送事件
EventBus.getDefault().post(new CollectEvent());複製程式碼

在正式講解之前,筆者覺得需要對一些基礎性的概念進行詳細的講解。眾所周知,EventBus沒出現之前,那時候的開發者一般是使用Android四大元件中的廣播進行元件間的訊息傳遞,那麼我們為什麼要使用事件匯流排機制來替代廣播呢?主要是因為:

廣播:耗時、容易被捕獲(不安全)。事件匯流排:更節省資源、更高效,能將資訊傳遞給原生以外的各種物件。

那麼,話又說回來了,事件匯流排又是什麼呢?

如下圖所示,事件匯流排機制透過記錄物件、使用觀察者模式來通知物件各種事件。(當然,你也可以傳送基本資料型別如 int,String 等作為一個事件)

對於事件匯流排EventBus而言,它的優缺點又是如何?這裡簡單總結下:

優點:開銷小,程式碼更優雅、簡潔,解耦傳送者和接收者,可動態設定事件處理執行緒和優先順序。缺點:每個事件必須自定義一個事件類,增加了維護成本。

EventBus是基於觀察者模式擴充套件而來的,我們先了解一下觀察者模式是什麼?

觀察者模式又可稱為釋出 - 訂閱模式,它定義了物件間的一種1對多的依賴關係,每當這個物件的狀態改變時,其它的物件都會接收到通知並被自動更新。

觀察者模式有以下角色:

抽象觀察者:將所有已註冊的觀察者物件儲存在一個集合中。具體觀察者:當內部狀態發生變化時,將會通知所有已註冊的觀察者。抽象觀察者:定義了一個更新介面,當被觀察者狀態改變時更新自己。具體觀察者:實現抽象觀察者的更新介面。

這裡筆者給出一個簡單的示例來讓大家更深一步理解觀察者模式的思想:

1、首先,建立抽象觀察者

public interface observer {    public void update(String message);}複製程式碼

2、接著,建立具體觀察者

public class WeXinUser implements observer {    private String name;    public WeXinUser(String name) {        this.name = name;    }    @Override    public void update(String message) {        ...    }}複製程式碼

3、然後,建立抽象被觀察者

public interface observable {    public void addWeXinUser(WeXinUser weXinUser);    public void removeWeXinUser(WeXinUser weXinUser);    public void notify(String message);}複製程式碼

4、最後,建立具體被觀察者

public class Subscription implements observable {    private List<WeXinUser> mUserList = new ArrayList();    @Override    public void addWeXinUser(WeXinUser weXinUser) {        mUserList.add(weXinUser);    }    @Override    public void removeWeXinUser(WeXinUser weXinUser) {        mUserList.remove(weXinUser);    }    @Override    public void notify(String message) {        for(WeXinUser weXinUser : mUserList) {            weXinUser.update(message);        }    }}複製程式碼

在具體使用時,我們便可以這樣使用,如下所示:

當然,EventBus的觀察者模式和一般的觀察者模式不同,它使用了擴充套件的觀察者模式對事件進行訂閱和分發,其實這裡的擴充套件就是指的使用了EventBus來作為中介者,抽離了許多職責,如下是它的官方原理圖:

在得知了EventBus的原理之後,我們注意到,每次我們在register之後,都必須進行一次unregister,這是為什麼呢?

因為register是強引用,它會讓物件無法得到記憶體回收,導致記憶體洩露。所以必須在unregister方法中釋放物件所佔的記憶體

有些同學可能之前使用的是EventBus2.x的版本,那麼它又與EventBus3.x的版本有哪些區別呢?

1、EventBus2.x使用的是執行時註解,它採用了反射的方式對整個註冊的類的所有方法進行掃描來完成註冊,因而會對效能有一定影響。2、EventBus3.x使用的是編譯時註解,Java檔案會編譯成.class檔案,再對class檔案進行打包等一系列處理。在編譯成.class檔案時,EventBus會使用EventBusAnnotationProcessor註解處理器讀取@Subscribe()註解並解析、處理其中的資訊,然後生成Java類來儲存所有訂閱者的訂閱資訊。這樣就創建出了對檔案或類的索引關係,並將其編入到apk中。3、從EventBus3.0開始使用了物件池快取減少了建立物件的開銷

除了EventBus,其實現在比較流行的事件匯流排還有RxBus,那麼,它與EventBus相比又如何呢?

1、RxJava的Observable有onError、onComplete等狀態回撥。2、Rxjava使用組合而非巢狀的方式,避免了回撥地獄。3、Rxjava的執行緒排程設計更加優秀,更簡單易用。4、Rxjava可使用多種運算子來進行鏈式呼叫來實現複雜的邏輯。5、Rxjava的資訊效率高於EventBus2.x,低於EventBus3.x

在瞭解了EventBus和RxBus的區別之後,那麼,對待新專案的事件匯流排選型時,我們該如何考量?

很簡單,如果專案中使用了RxJava,則使用RxBus,否則使用EventBus3.x

接下來將按以下順序來進行EventBus的原始碼分析:

1、訂閱者:EventBus.getDefault().register(this);2、釋出者:EventBus.getDefault().post(new CollectEvent());3、訂閱者:EventBus.getDefault().unregister(this)。

下面,我們正式開始EventBus的探索之旅~

二、EventBus.getDefault().register(this)

首先,我們從獲取EventBus例項的方法getDefault()開始分析:

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

在getDefault()中使用了雙重校驗並加鎖的單例模式來建立EventBus例項。

接著,我們看到EventBus的預設構造方法中做了什麼:

private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();public EventBus() {    this(DEFAULT_BUILDER);}複製程式碼

在EventBus的預設構造方法中又呼叫了它的另一個有參構造方法,將一個型別為EventBusBuilder的DEFAULT_BUILDER物件傳遞進去了。這裡的EventBusBuilder很明顯是一個EventBus的建造器,以便於EventBus能夠新增自定義的引數和安裝一個自定義的預設EventBus例項。

我們再看一下EventBusBuilder的構造方法:

public class EventBusBuilder {    ...    EventBusBuilder() {    }    ...}複製程式碼

EventBusBuilder的構造方法中什麼也沒有做,那我麼繼續檢視EventBus的這個有參構造方法:

private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;private final Map<Object, List<Class<?>>> typesBySubscriber;private final Map<Class<?>, Object> stickyEvents;EventBus(EventBusBuilder builder) {    ...    // 1    subscriptionsByEventType = new HashMap<>();    // 2    typesBySubscriber = new HashMap<>();    // 3    stickyEvents = new ConcurrentHashMap<>();    // 4    mainThreadSupport = builder.getMainThreadSupport();    mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;    backgroundPoster = new BackgroundPoster(this);    asyncPoster = new AsyncPoster(this);    ...    // 5    subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,            builder.strictMethodVerification, builder.ignoreGeneratedIndex);    // 從builder取中一些列訂閱相關資訊進行賦值    ...    // 6    executorService = builder.executorService;}複製程式碼

在註釋1處,建立了一個subscriptionsByEventType物件,可以看到它是一個型別為HashMap的subscriptionsByEventType物件,並且其key為 Event 型別,value為 Subscription連結串列。這裡的Subscription是一個訂閱資訊物件,它裡面儲存了兩個重要的欄位,一個是型別為 Object 的 subscriber,該欄位即為註冊的物件(在 Android 中時通常是 Activity物件);另一個是 型別為SubscriberMethod 的 subscriberMethod,它就是被@Subscribe註解的那個訂閱方法,裡面儲存了一個重要的欄位eventType,它是 Class<?> 型別的,代表了 Event 的型別。在註釋2處,新建了一個型別為 Map 的typesBySubscriber物件,它的key為subscriber物件,value為subscriber物件中所有的 Event 型別連結串列,日常使用中僅用於判斷某個物件是否註冊過。在註釋3處新建了一個型別為ConcurrentHashMap的stickyEvents物件,它是專用於粘性事件處理的一個欄位,key為事件的Class物件,value為當前的事件。可能有的同學不瞭解sticky event,這裡解釋下:

我們都知道普通事件是先註冊,然後傳送事件才能收到;而粘性事件,在傳送事件之後再訂閱該事件也能收到。並且,粘性事件會儲存在記憶體中,每次進入都會去記憶體中查詢獲取最新的粘性事件,除非你手動解除註冊

在註釋4處,新建了三個不同型別的事件傳送器,這裡總結下:

mainThreadPoster:主執行緒事件傳送器,透過它的mainThreadPoster.enqueue(subscription, event)方法可以將訂閱資訊和對應的事件進行入隊,然後透過 handler 去傳送一個訊息,在 handler 的 handleMessage 中去執行方法。backgroundPoster:後臺事件傳送器,透過它的enqueue() 將方法加入到後臺的一個佇列,最後透過執行緒池去執行,注意,它在 Executor的execute()方法 上添加了 synchronized關鍵字 並設立 了控制標記flag,保證任一時間只且僅能有一個任務會被執行緒池執行。asyncPoster:實現邏輯類似於backgroundPoster,不同於backgroundPoster的保證任一時間只且僅能有一個任務會被執行緒池執行的特性,asyncPoster則是非同步執行的,可以同時接收多個任務。

我們再回到註釋5這行程式碼,這裡新建了一個subscriberMethodFinder物件,這是從EventBus中抽離出的訂閱方法查詢的一個物件,在優秀的原始碼中,我們經常能看到組合優於繼承的這種實現思想。在註釋6處,從builder中取出了一個預設的執行緒池物件,它由Executors的newCachedThreadPool()方法建立,它是一個有則用、無則建立、無數量上限的執行緒池。

分析完這些核心的欄位之後,後面的講解就比較輕鬆了,接著我們檢視EventBus的regist()方法:

public void register(Object subscriber) {    Class<?> subscriberClass = subscriber.getClass();    // 1    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);    synchronized (this) {        for (SubscriberMethod subscriberMethod : subscriberMethods) {            // 2            subscribe(subscriber, subscriberMethod);        }    }}複製程式碼

在註釋1處,根據當前註冊類獲取 subscriberMethods這個訂閱方法列表 。在註釋2處,使用了增強for迴圈令subsciber物件 對 subscriberMethods 中每個 SubscriberMethod 進行訂閱。

接著我們檢視SubscriberMethodFinder的findSubscriberMethods()方法:

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {    // 1    List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);    if (subscriberMethods != null) {        return subscriberMethods;    }    // 2    if (ignoreGeneratedIndex) {        subscriberMethods = findUsingReflection(subscriberClass);    } else {        subscriberMethods = findUsingInfo(subscriberClass);    }    if (subscriberMethods.isEmpty()) {        throw new EventBusException("Subscriber " + subscriberClass                + " and its super classes have no public methods with the @Subscribe annotation");    } else {        METHOD_CACHE.put(subscriberClass, subscriberMethods);        return subscriberMethods;    }}複製程式碼

在註釋1處,如果快取中有subscriberClass物件對應 的訂閱方法列表,則直接返回。註釋2處,先詳細說說這個ignoreGeneratedIndex欄位, 它用來判斷是否使用生成的 APT 程式碼去最佳化尋找接收事件的過程,如果開啟了的話,那麼將會透過 subscriberInfoIndexes 來快速得到接收事件方法的相關資訊。如果我們沒有在專案中接入 EventBus 的 APT,那麼可以將 ignoreGeneratedIndex 欄位設為 false 以提高效能。這裡ignoreGeneratedIndex 預設為false,所以會執行findUsingInfo()方法,後面生成 subscriberMethods 成功的話會加入到快取中,失敗的話會 丟擲異常。

接著我們檢視SubscriberMethodFinder的findUsingInfo()方法:

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {    // 1    FindState findState = prepareFindState();    findState.initForSubscriber(subscriberClass);    // 2    while (findState.clazz != null) {        findState.subscriberInfo = getSubscriberInfo(findState);        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.moveToSuperclass();    }    // 4    return getMethodsAndRelease(findState);}複製程式碼

在註釋1處,呼叫了SubscriberMethodFinder的prepareFindState()方法建立了一個新的 FindState 類,我們來看看這個方法:

private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];private FindState prepareFindState() {    // 1    synchronized(FIND_STATE_POOL) {        for (int i = 0; i < POOL_SIZE; i++) {            FindState state = FIND_STATE_POOL[i];            if (state != null) {                FIND_STATE_POOL[i] = null;                return state;            }        }    }    // 2    return new FindState();}複製程式碼

在註釋1處,會先從 FIND_STATE_POOL 即 FindState 池中取出可用的 FindState(這裡的POOL_SIZE為4),如果沒有的話,則透過註釋2處的程式碼直接新建 一個新的 FindState 物件。

接著我們來分析下FindState這個類:

static class FindState {    ....    void initForSubscriber(Class<?> subscriberClass) {        this.subscriberClass = clazz = subscriberClass;        skipSuperClasses = false;        subscriberInfo = null;    }    ...}複製程式碼

它是 SubscriberMethodFinder 的內部類,這個方法主要做一個初始化、回收物件等工作。

我們接著回到SubscriberMethodFinder的註釋2處的SubscriberMethodFinder()方法:

private SubscriberInfo getSubscriberInfo(FindState findState) {    if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {        SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();        if (findState.clazz == superclassInfo.getSubscriberClass()) {            return superclassInfo;        }    }    if (subscriberInfoIndexes != null) {        for (SubscriberInfoIndex index: subscriberInfoIndexes) {            SubscriberInfo info = index.getSubscriberInfo(findState.clazz);            if (info != null) {                return info;            }        }    }    return null;}複製程式碼

在前面初始化的時候,findState的subscriberInfo和subscriberInfoIndexes 這兩個欄位為空,所以這裡直接返回 null。

接著我們檢視註釋3處的findUsingReflectionInSingleClass()方法:

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) {        methods = findState.clazz.getMethods();        findState.skipSuperClasses = true;    }    for (Method method: methods) {        int modifiers = method.getModifiers();        if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {            Class<?> [] parameterTypes = method.getParameterTypes();            if (parameterTypes.length == 1) {                Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);                if (subscribeAnnotation != null) {                    // 重點                    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");        }    }}複製程式碼

這個方法很長,大概做的事情是:

1、透過反射的方式獲取訂閱者類中的所有宣告方法,然後在這些方法裡面尋找以 @Subscribe作為註解的方法進行處理。2、在經過經過一輪檢查,看看 findState.subscriberMethods是否存在,如果沒有,將方法名,threadMode,優先順序,是否為 sticky 方法等資訊封裝到 SubscriberMethod 物件中,最後新增到 subscriberMethods 列表中

最後,我們繼續檢視註釋4處的getMethodsAndRelease()方法:

private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {    // 1    List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);    // 2    findState.recycle();    // 3    synchronized(FIND_STATE_POOL) {        for (int i = 0; i < POOL_SIZE; i++) {            if (FIND_STATE_POOL[i] == null) {                FIND_STATE_POOL[i] = findState;                break;            }        }    }    // 4    return subscriberMethods;}複製程式碼

在這裡,首先在註釋1處,從findState中取出了儲存的subscriberMethods。在註釋2處,將findState裡的儲存的所有物件進行回收。在註釋3處,把findState儲存在 FindState 池中方便下一次使用,以提高效能。最後,在註釋4處,返回subscriberMethods。接著,在EventBus的 register() 方法的最後會呼叫 subscribe 方法

public void register(Object subscriber) {    Class<?> subscriberClass = subscriber.getClass();    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);    synchronized (this) {        for (SubscriberMethod subscriberMethod : subscriberMethods) {            subscribe(subscriber, subscriberMethod);        }    }}複製程式碼

我們繼續看看這個subscribe()方法做的事情:

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {    Class<?> eventType = subscriberMethod.eventType;    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);    // 1    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);        }    }    int size = subscriptions.size();    // 2    for (int i = 0; i <= size; i++) {        if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {            subscriptions.add(i, newSubscription);            break;        }    }    // 3    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);    if (subscribedEvents == null) {        subscribedEvents = new ArrayList<>();        typesBySubscriber.put(subscriber, subscribedEvents);    }    subscribedEvents.add(eventType);    // 4    if (subscriberMethod.sticky) {        if (eventInheritance) {            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);        }    }}複製程式碼

首先,在註釋1處,會根據 subscriberMethod的eventType,在 subscriptionsByEventType 去查詢一個 CopyOnWriteArrayList ,如果沒有則建立一個新的 CopyOnWriteArrayList,然後將這個 CopyOnWriteArrayList 放入 subscriptionsByEventType 中。在註釋2處,新增 newSubscription物件,它是一個 Subscription 類,裡面包含著 subscriber 和 subscriberMethod 等資訊,並且這裡有一個優先順序的判斷,說明它是按照優先順序新增的。優先順序越高,會到在當前 List 靠前面的位置。在註釋3處,對typesBySubscriber 進行新增,這主要是在EventBus的isRegister()方法中去使用的,目的是用來判斷這個 Subscriber物件 是否已被註冊過。最後,在註釋4處,會判斷是否是 sticky事件。如果是sticky事件的話,會呼叫 checkPostStickyEventToSubscription() 方法。

我們接著檢視這個checkPostStickyEventToSubscription()方法:

private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {    if (stickyEvent != null) {        postToSubscription(newSubscription, stickyEvent, isMainThread());    }}複製程式碼

可以看到最終是呼叫了postToSubscription()這個方法來進行粘性事件的傳送,對於粘性事件的處理,我們最後再分析,接下來我們看看事件是如何post的。

三、EventBus.getDefault().post(new CollectEvent())
public void post(Object event) {    // 1    PostingThreadState postingState = currentPostingThreadState.get();    List <Object> eventQueue = postingState.eventQueue;    eventQueue.add(event);    // 2    if (!postingState.isPosting) {        postingState.isMainThread = isMainThread();        postingState.isPosting = true;        if (postingState.canceled) {            throw new EventBusException("Internal error. Abort state was not reset");        }        try {            while (!eventQueue.isEmpty()) {                postSingleEvent(eventQueue.remove(0), postingState);            }        } finally {            postingState.isPosting = false;            postingState.isMainThread = false;        }    }}複製程式碼

註釋1處,這裡的currentPostingThreadState 是一個 ThreadLocal 型別的物件,裡面儲存了 PostingThreadState,而 PostingThreadState 中包含了一個 eventQueue 和其他一些標誌位,相關的原始碼如下:

private final ThreadLocal <PostingThreadState> currentPostingThreadState = new ThreadLocal <PostingThreadState> () {@Overrideprotected PostingThreadState initialValue() {    return new PostingThreadState();}};final static class PostingThreadState {    final List <Object> eventQueue = new ArrayList<>();    boolean isPosting;    boolean isMainThread;    Subscription subscription;    Object event;    boolean canceled;}複製程式碼

接著把傳入的 event,儲存到了當前執行緒中的一個變數 PostingThreadState 的 eventQueue 中。在註釋2處,最後呼叫了 postSingleEvent() 方法,我們繼續檢視這個方法:

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {    Class<?> eventClass = event.getClass();    boolean subscriptionFound = false;    // 1    if (eventInheritance) {        // 2        List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);        int countTypes = eventTypes.size();        for (int h = 0; h < countTypes; h++) {            Class<?> clazz = eventTypes.get(h);            subscriptionFound |=            // 3            postSingleEventForEventType(event, postingState, clazz);        }    } else {        subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);    }    if (!subscriptionFound) {        ...    }}複製程式碼

首先,在註釋1處,首先取出 Event 的 class 型別,接著會對 eventInheritance 標誌位 判斷,它預設為true,如果設為 true 的話,它會在發射事件的時候判斷是否需要發射父類事件,設為 false,能夠提高一些效能。接著,在註釋2處,會呼叫lookupAllEventTypes() 方法,它的作用就是取出 Event 及其父類和介面的 class 列表,當然重複取的話會影響效能,所以它也做了一個 eventTypesCache 的快取,這樣就不用重複呼叫 getSuperclass() 方法。最後,在註釋3處會呼叫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;}複製程式碼

可以看到,這裡直接根據 Event 型別從 subscriptionsByEventType 中取出對應的 subscriptions物件,最後呼叫了 postToSubscription() 方法。

這個時候我們再看看這個postToSubscription()方法:

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 {                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("Unknow thread mode: " + subscription.subscriberMethod.threadMode);    }}複製程式碼

從上面可以看出,這裡透過threadMode 來判斷在哪個執行緒中去執行方法:

1、POSTING:執行 invokeSubscriber() 方法,內部直接採用反射呼叫。2、MAIN:首先去判斷當前是否在 UI 執行緒,如果是的話則直接反射呼叫,否則呼叫mainThreadPoster的enqueue()方法,即把當前的方法加入到佇列之中,然後透過 handler 去傳送一個訊息,在 handler 的 handleMessage 中去執行方法。3、MAIN_ORDERED:與MAIN類似,不過是確保是順序執行的。4、BACKGROUND:判斷當前是否在 UI 執行緒,如果不是的話則直接反射呼叫,是的話透過backgroundPoster的enqueue()方法 將方法加入到後臺的一個佇列,最後透過執行緒池去執行。注意,backgroundPoster在 Executor的execute()方法 上添加了 synchronized關鍵字 並設立 了控制標記flag,保證任一時間只且僅能有一個任務會被執行緒池執行。5、ASYNC:邏輯實現類似於BACKGROUND,將任務加入到後臺的一個佇列,最終由Eventbus 中的一個執行緒池去呼叫,這裡的執行緒池與 BACKGROUND 邏輯中的執行緒池用的是同一個,即使用Executors的newCachedThreadPool()方法建立的執行緒池,它是一個有則用、無則建立、無數量上限的執行緒池。不同於backgroundPoster的保證任一時間只且僅能有一個任務會被執行緒池執行的特性,這裡asyncPoster則是非同步執行的,可以同時接收多個任務

分析完EventBus的post()方法值,我們接著看看它的unregister()。

四、EventBus.getDefault().unregister(this)

它的核心原始碼如下所示:

public synchronized void unregister(Object subscriber) {    List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);    if (subscribedTypes != null) {        for (Class<?> eventType : subscribedTypes) {            //1            unsubscribeByEventType(subscriber, eventType);        }        // 2        typesBySubscriber.remove(subscriber);    }}複製程式碼

首先,在註釋1處,unsubscribeByEventType() 方法中對 subscriptionsByEventType 移除了該 subscriber 的所有訂閱資訊。最後,在註釋2處,移除了註冊物件和其對應的所有 Event 事件連結串列

最後,我們在來分析下EventBus中對粘性事件的處理。

五、EventBus.getDefault.postSticky(new CollectEvent())

如果想要發射 sticky 事件需要透過 EventBus的postSticky() 方法,內部原始碼如下所示:

public void postSticky(Object event) {    synchronized (stickyEvents) {        // 1        stickyEvents.put(event.getClass(), event);    }    // 2    post(event);}複製程式碼

在註釋1處,先將該事件放入 stickyEvents 中,接著在註釋2處使用post()傳送事件。前面我們在分析register()方法的最後部分時,其中有關粘性事件的原始碼如下:

if (subscriberMethod.sticky) {    Object stickyEvent = stickyEvents.get(eventType);    if (stickyEvent != null) {        postToSubscription(newSubscription, stickyEvent, isMainThread());    }}複製程式碼

可以看到,在這裡會判斷當前事件是否是 sticky 事件,如果是,則從 stickyEvents 中拿出該事件並執行 postToSubscription() 方法

至此,EventBus原始碼分析完畢。

六、總結

EventBus 的原始碼在Android主流三方庫原始碼分析系列中可以說是除了ButterKnife之外,算是比較簡單的了。但是,它其中的一些思想和設計是值得借鑑的。比如它使用 FindState 複用池來複用 FindState 物件,在各處使用了 synchronized 關鍵字進行程式碼塊同步的一些最佳化操作。其中上面分析了這麼多,EventBus最核心的邏輯就是利用了 subscriptionsByEventType 這個重要的列表,將訂閱物件,即接收事件的方法儲存在這個列表,釋出事件的時候在列表中查詢出相對應的方法並執行

設計思想解讀開源框架

Android相關原始碼解析

Android相關原始碼解析之EventBus解析

12
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • Linux網路之 從 C10K 到 DPDK