Android主流三方庫源碼分析(9、深刻理解EventBus源碼)

前言

成爲一名優秀的Android開發,須要一份完備的知識體系,在這裏,讓咱們一塊兒成長爲本身所想的那樣~。

不知不覺,Android主流三方庫源碼分析系列已經要接近尾聲了。這一次,筆者將會對Android中的事件總線框架EventBus源碼進行詳細地解析,一塊兒來和你們揭開它背後的面紗。java

1、簡單示例

一、首先,定義要傳遞的事件實體

public class CollectEvent { ... }
複製代碼

二、準備訂閱者:聲明並註解你的訂閱方法

@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(CollectEvent event) {
    LogHelper.d("OK");
}
複製代碼

三、在2中,也就是訂閱中所在的類中,註冊和解註冊你的訂閱者

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

@Override
public void onStop() {
    super.onStop();
    EventBus.getDefault().unregister(this);
}
複製代碼

四、發送事件

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

在正式講解以前,筆者以爲須要對一些基礎性的概念進行詳細的講解。衆所周知,EventBus沒出現以前,那時候的開發者通常是使用Android四大組件中的廣播進行組件間的消息傳遞,那麼咱們爲何要使用事件總線機制來替代廣播呢?主要是由於:git

  • 廣播:耗時、容易被捕獲(不安全)。
  • 事件總線:更節省資源、更高效,能將信息傳遞給原生之外的各類對象。

那麼,話又說回來了,事件總線又是什麼呢?github

以下圖所示,事件總線機制經過記錄對象、使用觀察者模式來通知對象各類事件。(固然,你也能夠發送基本數據類型如 int,String 等做爲一個事件)json

image

對於事件總線EventBus而言,它的優缺點又是如何?這裏簡單總結下:緩存

  • 優勢:開銷小,代碼更優雅、簡潔,解耦發送者和接收者,可動態設置事件處理線程和優先級。
  • 缺點:每一個事件必須自定義一個事件類,增長了維護成本。

EventBus是基於觀察者模式擴展而來的,咱們先了解一下觀察者模式是什麼?安全

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

觀察者模式有如下角色:架構

  • 抽象被觀察者:將全部已註冊的觀察者對象保存在一個集合中。
  • 具體被觀察者:當內部狀態發生變化時,將會通知全部已註冊的觀察者。
  • 抽象觀察者:定義了一個更新接口,當被觀察者狀態改變時更新本身。
  • 具體被觀察者:實現抽象觀察者的更新接口。

這裏筆者給出一個簡單的示例來讓你們更深一步理解觀察者模式的思想:框架

一、首先,建立抽象觀察者異步

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

二、接着,建立具體觀察者

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

三、而後,建立抽象被觀察者

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

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

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);
        }
    }
}
複製代碼

在具體使用時,咱們即可以這樣使用,以下所示:

Subscription subscription = new Subscription();

WeXinUser hongYang = new WeXinUser("HongYang");
WeXinUser rengYuGang = new WeXinUser("RengYuGang");
WeXinUser liuWangShu = new WeXinUser("LiuWangShu");

subscription.addWeiXinUser(hongYang);
subscription.addWeiXinUser(rengYuGang);
subscription.addWeiXinUser(liuWangShu);
subscription.notify("New article coming");
複製代碼

在這裏,hongYang、rengYuGang、liuWangShu等大神都訂閱了個人微信公衆號,每當個人公衆號發表文章時(subscription.notify()),他們就會接收到最新的文章信息(weXinUser.update())。(ps:固然,這一切都是YY,事實是,我並無微信公衆號-0v0-)

固然,EventBus的觀察者模式和通常的觀察者模式不一樣,它使用了擴展的觀察者模式對事件進行訂閱和分發,其實這裏的擴展就是指的使用了EventBus來做爲中介者,抽離了許多職責,以下是它的官方原理圖:

image

在得知了EventBus的原理以後,咱們注意到,每次咱們在register以後,都必須進行一次unregister,這是爲何呢?

由於register是強引用,它會讓對象沒法獲得內存回收,致使內存泄露。因此必須在unregister方法中釋放對象所佔的內存

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

  • 一、EventBus2.x使用的是運行時註解,它採用了反射的方式對整個註冊的類的全部方法進行掃描來完成註冊,於是會對性能有必定影響
  • 二、EventBus3.x使用的是編譯時註解,Java文件會編譯成.class文件,再對class文件進行打包等一系列處理。在編譯成.class文件時,EventBus會使用EventBusAnnotationProcessor註解處理器讀取@Subscribe()註解並解析、處理其中的信息,而後生成Java類來保存全部訂閱者的訂閱信息。這樣就建立出了對文件或類的索引關係,並將其編入到apk中
  • 三、從EventBus3.0開始使用了對象池緩存減小了建立對象的開銷

除了EventBus,其實如今比較流行的事件總線還有RxBus,那麼,它與EventBus相比又如何呢?

  • 一、RxJava的Observable有onError、onComplete等狀態回調
  • 二、Rxjava使用組合而非嵌套的方式,避免了回調地獄
  • 三、Rxjava的線程調度設計的更加優秀,更簡單易用
  • 四、Rxjava可以使用多種操做符來進行鏈式調用來實現複雜的邏輯
  • 五、Rxjava的信息效率高於EventBus2.x,低於EventBus3.x

在瞭解了EventBus和RxBus的區別以後,那麼,對待新項目的事件總線選型時,咱們該如何考量?

很簡單,若是項目中使用了RxJava,則使用RxBus,不然使用EventBus3.x

接下來將按如下順序來進行EventBus的源碼分析:

  • 一、訂閱者:EventBus.getDefault().register(this);
  • 二、發佈者:EventBus.getDefault().post(new CollectEvent());
  • 三、訂閱者:EventBus.getDefault().unregister(this)。

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

2、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");
        }
    }
}
複製代碼

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

  • 一、經過反射的方式獲取訂閱者類中的全部聲明方法,而後在這些方法裏面尋找以 @Subscribe做爲註解的方法進行處理
  • 二、在通過通過一輪檢查,看看 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的。

3、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> () {
@Override
protected 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 來判斷在哪一個線程中去執行方法:

  • 一、POSTING:執行 invokeSubscriber() 方法,內部直接採用反射調用
  • 二、MAIN:首先去判斷當前是否在 UI 線程,若是是的話則直接反射調用,不然調用mainThreadPoster的enqueue()方法,即把當前的方法加入到隊列之中,而後經過 handler 去發送一個消息,在 handler 的 handleMessage 中去執行方法
  • 三、MAIN_ORDERED:與MAIN相似,不過是確保是順序執行的
  • 四、BACKGROUND:判斷當前是否在 UI 線程,若是不是的話則直接反射調用,是的話經過backgroundPoster的enqueue()方法 將方法加入到後臺的一個隊列,最後經過線程池去執行。注意,backgroundPoster在 Executor的execute()方法 上添加了 synchronized關鍵字 並設立 了控制標記flag,保證任一時間只且僅能有一個任務會被線程池執行
  • 五、ASYNC:邏輯實現相似於BACKGROUND,將任務加入到後臺的一個隊列,最終由Eventbus 中的一個線程池去調用,這裏的線程池與 BACKGROUND 邏輯中的線程池用的是同一個,即便用Executors的newCachedThreadPool()方法建立的線程池,它是一個有則用、無則建立、無數量上限的線程池。不一樣於backgroundPoster的保證任一時間只且僅能有一個任務會被線程池執行的特性,這裏asyncPoster則是異步運行的,能夠同時接收多個任務

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

4、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中對粘性事件的處理。

5、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源碼分析完畢。

6、總結

EventBus 的源碼在Android主流三方庫源碼分析系列中能夠說是除了ButterKnife以外,算是比較簡單的了。可是,它其中的一些思想和設計是值得借鑑的。好比它使用 FindState 複用池來複用 FindState 對象,在各處使用了 synchronized 關鍵字進行代碼塊同步的一些優化操做。其中上面分析了這麼多,EventBus最核心的邏輯就是利用了 subscriptionsByEventType 這個重要的列表,將訂閱對象,即接收事件的方法存儲在這個列表,發佈事件的時候在列表中查詢出相對應的方法並執行。至此,Android主流三方庫源碼分析系列到此完結~

想象一個來自將來的本身,他很是自信,很是成功,
擁有你如今所但願的一切,他會對如今的你說些什麼?
他怎麼說,你就怎麼去作,10年以後,你就變成了他。
複製代碼
參考連接:

一、EventBus V3.1.1 源碼

二、Android進階之光

三、Android組件化架構

三、EventBus設計之禪

四、從源碼入手來學習EventBus 3事件總線機制

讚揚

若是這個庫對您有很大幫助,您願意支持這個項目的進一步開發和這個項目的持續維護。你能夠掃描下面的二維碼,讓我喝一杯咖啡或啤酒。很是感謝您的捐贈。謝謝!


Contanct Me

● 微信:

歡迎關注個人微信:bcce5360

● 微信羣:

微信羣若是不能掃碼加入,麻煩你們想進微信羣的朋友們,加我微信拉你進羣。

● QQ羣:

2千人QQ羣,Awesome-Android學習交流羣,QQ羣號:959936182, 歡迎你們加入~

About me

很感謝您閱讀這篇文章,但願您能將它分享給您的朋友或技術羣,這對我意義重大。

但願咱們能成爲朋友,在 Github掘金上一塊兒分享知識。

相關文章
相關標籤/搜索