EventBus使用與源碼解析

使用篇

1.添加依賴庫
 compile 'org.greenrobot:eventbus:3.0.0'
2.注冊、訂閱、取消訂閱事件
EventBus.getDefault().register(obj);//注冊
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)//sticky:true為黏性事件,false為一般事件
public void onMessageReceive(Object obj) {//訂閱
       //onMessageReceive這個方法名可以任意取,obj為發布者發布的事件類型
       //省略...
   }
EventBus.getDefault().unregister(obj);//取消訂閱
3.事件發布者發布事件
EventBus.getDefault().post(obj);//一般事件
EventBus.getDefault().postSticky(obj);//黏性事件
4.混淆
-keepattributes *Annotation*
-keepclassmembers class **{
    @org.greenrobot.eventbus.Suscribe<methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode{*;}
#only required if you use AsyncExecutor
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent{
    <init>(java.lang.Throwable);
}

源碼解析篇

當我們使用EventBus時,首先會調用EventBus.getDefault()來獲取EventBus實例,下面看getDefault方法:
EventBus#getDefault

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

很明顯,這是采用DCL雙重檢查的單例設計模式,下面看它的構造方法:

//EventBus的建造者模式builder,通過它為EventBus設置各種默認屬性
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
public EventBus() {
        this(DEFAULT_BUILDER);//DEFAULT_BUILDER為EventBus的建造者模式builder
    }
EventBus(EventBusBuilder builder) {
        //private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
        //訂閱者與訂閱事件的集合,注意:一個訂閱者可以訂閱多個訂閱事件
        //因此是一個訂閱者和若干個訂閱事件
        subscriptionsByEventType = new HashMap<>();
        //private final Map<Object, List<Class<?>>> typesBySubscriber;
        //訂閱事件類型,根據訂閱對象輕松找到訂閱者類,用于取消訂閱時
        typesBySubscriber = new HashMap<>();
        //private final Map<Class<?>, Object> stickyEvents;
        //訂閱者和訂閱事件,黏性事件
        stickyEvents = new ConcurrentHashMap<>();
        //這是一個handler,用于排隊發布事件與取出發布事件來消費
        mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
        //后臺線程
        backgroundPoster = new BackgroundPoster(this);
        //異步線程
        asyncPoster = new AsyncPoster(this);
        indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
        //private final SubscriberMethodFinder subscriberMethodFinder;
        //訂閱者方法的查找類,這個類很重要,用于查找訂閱者的訂閱方法(集合)
        subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
                builder.strictMethodVerification, builder.ignoreGeneratedIndex);
        //省略...
    }

通過構造方法,它調用了this(DEFAULT_BUILDER),這里的DEFAULT_BUILDER就是EventBusBuilder,可以看出,它通過建造者模式對EventBus進行各種配置。這里就不貼EventBusBuilder的代碼了,可自行閱讀EventBusBuilder源碼。
在獲取了EventBus實例后,會調用注冊方法,把訂閱者注冊到EventBus中。下面看這個注冊方法:
EventBus#register

public void register(Object subscriber) {
        //獲取訂閱者類class
        Class<?> subscriberClass = subscriber.getClass();
        //通過訂閱者方法查找類的查找方法,返回訂閱方法SubscriberMethod集合
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {//遍歷訂閱方法集合
                subscribe(subscriber, subscriberMethod);//對每個訂閱者進行注冊
            }
        }
    }

通過閱讀register源碼,我們不難看出它整體思路通過訂閱者class,獲取該訂閱者里面所有的訂閱方法,然后開啟同步鎖并遍歷訂閱方法,對每一個訂閱方法進行注冊操作。
這里要補充說明一下SubscriberMethod(訂閱方法)里面的幾個屬性:

public class SubscriberMethod {
    final Method method;//訂閱者定義的方法
    //ThreadMode為枚舉類型,取值如下:POSTING,MAIN,BACKGROUND,ASYNC
    final ThreadMode threadMode;//訂閱方法工作的線程
    final Class<?> eventType;//EventBus發布(post)的事件類型,即訂閱方法接收的數據類型
    final int priority;//訂閱方法優先級
    final boolean sticky;//是否為黏性方法
    //其余部分省略...
}

下面看看訂閱者方法查找類究竟是如何查找訂閱方法的?
SubscriberMethodFinder#findSubscriberMethods

//METHOD_CACHE是緩存,key是訂閱者,value是訂閱方法集合
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
//是否忽略注解器生成的XxxEventBus
private final boolean ignoreGeneratedIndex;
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        //從緩存中獲取訂閱者類class的訂閱方法
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {//一旦在緩存中找到,就不再查找,直接返回訂閱方法集合
            return subscriberMethods;
        }
        //ignoreGeneratedIndex默認為false,也可以通過EventBusBuilder來設置。
        //這里因為我們是通過EventBus.getDefault()方法來獲取的實例
        //所以ignoreGeneratedIndex是默認false的情況,即執行else情況
        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 {
            //把訂閱者類class與訂閱方法集合存入緩存,方便下次調用不再繼續查找,直接緩存讀取
            METHOD_CACHE.put(subscriberClass, subscriberMethods);
            return subscriberMethods;
        }
    }

閱讀上面源碼可知:首先從緩存METHOD_CACHE中查找訂閱方法集合,找到立即返回,后面不會再執行;否則,根據ignoreGeneratedIndex是否為true,執行查找獲取對應的訂閱方法集合;接著對找到的訂閱方法集合判空,如果是空,則拋出異常,不為空時,則把訂閱者與對應的訂閱方法集合存入緩存,方便下次調用不需重新查找,最后返回訂閱方法集合。
從上面源碼可知,查找訂閱方法由findUsingInfo方法執行,下面看該方法:
SubscriberMethodFinder#findUsingInfo

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        FindState findState = prepareFindState();//返回FindState實例
        findState.initForSubscriber(subscriberClass);//初始化FindState
        while (findState.clazz != null) {//當findState.clazz == null時結束循環
            //獲取訂閱者信息,并賦值給findState.subscriberInfo
            findState.subscriberInfo = getSubscriberInfo(findState);
            if (findState.subscriberInfo != null) {//對findState.subscriberInfo進行判空
                SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
                for (SubscriberMethod subscriberMethod : array) {
                    if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                        findState.subscriberMethods.add(subscriberMethod);
                    }
                }
            } else {
                findUsingReflectionInSingleClass(findState);
            }
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }

觀察findUsingInfo方法,首先執行的是prepareFindState方法,返回查找狀態類FindState的實例。
這里需補充一下說明FindState,它是SubscriberMethodFinder的靜態內部類:
SubscriberMethodFinder靜態內部類FindState

static class FindState {
        //訂閱方法
        final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
        final Map<Class, Object> anyMethodByEventType = new HashMap<>();
        final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
        final StringBuilder methodKeyBuilder = new StringBuilder(128);

        Class<?> subscriberClass;//訂閱者class
        Class<?> clazz;//訂閱者class
        boolean skipSuperClasses;//是否跳過檢查父類class
        SubscriberInfo subscriberInfo;//訂閱者信息
        //其他略...
}

下面看prepareFindState方法是如何返回查找狀態類FindState實例的:
SubscriberMethodFinder#prepareFindState

private static final int POOL_SIZE = 4;
//定義一個容量為4的查找狀態數組
private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];
private FindState prepareFindState() {
        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;
                }
            }
        }
        return new FindState();//在數組內的查找狀態均為空時,新建一個查找狀態實例返回
    }

在獲得FindState實例后,調用initForSubscriber初始化該狀態:
FindState#initForSubscriber

void initForSubscriber(Class<?> subscriberClass) {
     this.subscriberClass = clazz = subscriberClass;//把訂閱者class賦值給subscriberClass和clazz
     skipSuperClasses = false;
     subscriberInfo = null;
}

之后在findState.clazz不為空的條件下循環,直到findState.clazz為空結束循環,并通過調用getMethodsAndRelease方法返回List<SubscriberMethod>。
先看循環體內邏輯,調用getSubscriberInfo方法,返回查找狀態的訂閱者信息findState.subscriberInfo,依據該信息是否為空,執行相應的操作。還是看看getSubscriberInfo方法:
SubscriberMethodFinder#getSubscriberInfo

private List<SubscriberInfoIndex> subscriberInfoIndexes;
private SubscriberInfo getSubscriberInfo(FindState findState) {
        //通過FindState#initForSubscriber方法 => subscriberInfo = null可知,不會執行if語句塊
        if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
            SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
            if (findState.clazz == superclassInfo.getSubscriberClass()) {
                return superclassInfo;
            }
        }
        //subscriberInfoIndexes到目前為止,均未被賦值,因此也是null,因此也不會執行if語句塊
        if (subscriberInfoIndexes != null) {
            for (SubscriberInfoIndex index : subscriberInfoIndexes) {
                SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
                if (info != null) {
                    return info;
                }
            }
        }
        return null;//有上面可知,最終執行返回null
    }

該方法,首先是判空:在傳入的查找狀態findState中存儲的訂閱信息不為空且其父類定閱信息不為空的條件下,獲取其父類訂閱信息superclassInfo,然后在傳入的訂閱信息與其父類訂閱信息一致的情況下,返回該父類訂閱信息;第二次判空:當subscriberInfoIndexes不為空時,對數組subscriberInfoIndexes進行遍歷,在訂閱者信息不為空時返回,不在往下執行;在兩次判空均為null時,直接返回null(訂閱者信息)。
對于getSubscriberInfo方法,首先傳入的findState就是上面通過初始化之后的狀態,即findState.subscriberInfo為空,接著由EventBus的構造方法subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,builder.strictMethodVerification, builder.ignoreGeneratedIndex)可知,subscriberInfoIndexes也是空,所以最終返回的訂閱者信息是空,所以findUsingInfo方法的循環體里面的判斷語句執行else語句:findUsingReflectionInSingleClass(findState)。
SubscriberMethodFinder#findUsingReflectionInSingleClass

private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
        try {
            //通過反射獲取訂閱者所有方法(public + private)
            methods = findState.clazz.getDeclaredMethods();
        } catch (Throwable th) {
            //通過反射獲取訂閱者所有方法(public)
            methods = findState.clazz.getMethods();
            findState.skipSuperClasses = true;
        }
        for (Method method : methods) {//遍歷所有的方法
            int modifiers = method.getModifiers();//獲取方法的修飾符
            //只有方法修飾符是public且不是abstract或static才能進入執行if語句塊
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                //獲取方法的參數類型數組
                Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length == 1) {//只有當方法的參數為一個時,才能執行if語句塊
                    //滿足上面所有條件的方法獲取方法注解@Subscribe
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) {//當獲取的注解不是空時,此方法才是訂閱者類的訂閱方法
                        //獲取方法參數類型,即post(Object event)的event類型
                        Class<?> eventType = parameterTypes[0];
                        if (findState.checkAdd(method, eventType)) {//當訂閱者與訂閱方添加成功后
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            //把訂閱方法添加到findState.subscriberMethods里面
                            findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                        }
                    }
                //默認情況下strictMethodVerification=false,因此if語句塊不會執行。
                } 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);
                }
            //默認情況下strictMethodVerification=false,因此if語句塊不會執行。
            } 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");
            }
        }
    }

通過閱讀findUsingReflectionInSingleClass源碼,我們知道:首先通過反射獲取訂閱者類的所有方法,然后對方法進行遍歷,只有當方法修飾符為public、方法參數只有一個且該方法存在@Subscribe注解時,該方法才是訂閱者類的訂閱方法,接著獲取@Subscribe注解實例(不為空時),把訂閱方法(包括方法,訂閱者,線程模式,優先級,是否黏性事件)添加到findState.subscriberMethods里面。
分析完findUsingReflectionInSingleClass源碼,接著就是findState.moveToSuperclass:
FindState#moveToSuperclass

void moveToSuperclass() {
            if (skipSuperClasses) {//跳過父類方法檢測
                clazz = null;
            } else {//檢測父類方法時
                clazz = clazz.getSuperclass();
                String clazzName = clazz.getName();
                /** Skip system classes, this just degrades performance. */
                if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) {
                    clazz = null;
                }
            }
        }

從這段源碼可知:當skipSuperClasses為true,或者父類方法是系統方法時,置clazz為null,其他時候置clazz為clazz.getSuperclass()。
最后就是跳出循環體,通過調用getMethodsAndRelease方法,返回訂閱方法集合了。
SubscriberMethodFinder#getMethodsAndRelease

private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
        List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
        findState.recycle();
        synchronized (FIND_STATE_POOL) {
            for (int i = 0; i < POOL_SIZE; i++) {
                if (FIND_STATE_POOL[i] == null) {
                    FIND_STATE_POOL[i] = findState;
                    break;
                }
            }
        }
        return subscriberMethods;
    }

這一段源碼很簡單,就是通過findState.subscriberMethods新建一個訂閱者方法集合并最終返回,接著重置findState,并把之前的findState設置到FIND_STATE_POOL數組中。
分析完查找訂閱者的訂閱方法之后,回到EventBus的register方法,采用同步鎖并遍歷訂閱方法集合,對找到的每一個訂閱方法與訂閱者進行注冊操作。
EventBus#subscribe

    // Must be called in synchronized block必須在同步代碼塊中調用
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        //首先獲取EventBus的post數據類型
        Class<?> eventType = subscriberMethod.eventType;
        //創建一個訂閱事件(包括訂閱者和訂閱方法)
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        //Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType
        //以發布的數據類型為key,獲取線程安全的發布事件集合
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions == null) {//當集合為空時,new一個,并把發布數據類型與發布事件存入緩存
            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();
        for (int i = 0; i <= size; i++) {
            //當到達集合末尾或此發布事件的優先級高于前一個,則在末尾或該一事件前插入,之后跳出循環
            if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                subscriptions.add(i, newSubscription);
                break;
            }
        }

        //Map<Object, List<Class<?>>> typesBySubscriber
        //從緩存中根據key(訂閱者)取出value(發布事件類型)
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {//如果集合為空,創建一個
            subscribedEvents = new ArrayList<>();
            //把訂閱者與發布的訂閱事件集合存入緩存
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);//把發布的訂閱事件存入集合

        if (subscriberMethod.sticky) {//根據是否黏性事件
            if (eventInheritance) {//默認是true,事件是否有繼承性
                // 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 {
                //Map<Class<?>, Object> stickyEvents
                //緩存中根據發布的事件類型class,取出原發布事件
                Object stickyEvent = stickyEvents.get(eventType);
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    }

剖析該方法前,先補充說明一下訂閱事件Subscription這個類:

final class Subscription {
    final Object subscriber;//訂閱者類
    final SubscriberMethod subscriberMethod;//訂閱方法(方法,線程,發布事件類型,優先級,黏性等)
    //一旦EventBus#unregister被調用,active就會變成false,
    //這將通過隊列事件傳遞EventBus#invokeSubscriber來檢查,以防止競態條件。
    volatile boolean active;
}

了解完訂閱事件Subscription后,再閱讀subscribe方法源碼:首先是根據訂閱方法,獲取發布數據類型class,接著創建訂閱事件,根據訂閱事件集合是否為空,空則創建集合,并把發布數據class與訂閱事件集合存入緩存;不為空則拋重復注冊異常;接著根據訂閱事件的優先級或是否到達末尾,把訂閱事件插入適當的位置;然后根據發布數據class集合是否為空,空則創建,并把訂閱者與發布事件集合存入緩存,不為空時,把發布的事件存入發布事件集合。最后根據是否為黏性事件完成事件注冊。
下面看checkPostStickyEventToSubscription方法:
EventBus#checkPostStickyEventToSubscription

private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
        if (stickyEvent != null) {//stickyEvent必不為空
            postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());
        }
    }

//根據訂閱事件的線程模式執行不同的方法,第三個參數是判斷是否為主線程
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.threadMode) {
            case POSTING://在哪個線程post就在哪個線程接收事件并處理
                invokeSubscriber(subscription, event);//反射調用method方法
                break;
            case MAIN://僅在主線程接收處理
                if (isMainThread) {
                    invokeSubscriber(subscription, event);//反射調用method方法
                } else {
                    mainThreadPoster.enqueue(subscription, event);//handler處理
                }
                break;
            case BACKGROUND://僅在后臺程接收處理
                if (isMainThread) {
                    backgroundPoster.enqueue(subscription, event);//線程池處理
                } else {
                    invokeSubscriber(subscription, event);//反射調用method方法
                }
                break;
            case ASYNC://另啟線程(workthread)接收處理
                asyncPoster.enqueue(subscription, event);//線程池處理
                break;
            default://不屬于以上線程,直接拋未知線程異常
                throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
        }
    }

至此,EventBus的register方法分析完畢,下面看post方法:
EventBus#post

public void post(Object event) {
        //獲取發送線程的實例postingState
        PostingThreadState postingState = currentPostingThreadState.get();
        //獲取派送事件隊列,并把事件加入隊列
        List<Object> eventQueue = postingState.eventQueue;
        eventQueue.add(event);

        if (!postingState.isPosting) {//事件未派送狀態
            //判斷是否UI線程
            postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
            postingState.isPosting = true;//事件派送狀態標志位置為true
            if (postingState.canceled) {//正在派送狀態,如果被取消,拋內部異常
                throw new EventBusException("Internal error. Abort state was not reset");
            }
            try {
                while (!eventQueue.isEmpty()) {
                    //當派送事件隊列不為空時,對第一個事件進行派送,并移除該事件
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {//最終都會把是否在派送標志位和UI線程標志位置為false
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }
//ThreadLocal可保證不同線程值多樣性(線程獨享),這里僅創建了一個PostingThreadState實例
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
        @Override
        protected PostingThreadState initialValue() {
            return new 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;//標志位,是否被取消
    }

post方法很簡單,大體思路:首先獲取派送事件狀態,然后把要派送的是按放入該派送事件狀態的事件隊列中,根據標志位未派送狀態,對事件隊列中的事件逐一取出并進行派送,并且最終把標志位是否被派送和UI線程置為false,下面看postSingleEvent方法是如何進行事件派送的:
EventBus#postSingleEvent

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;//默認訂閱處理器未發現處理方法
        //默認為true,表示訂閱事件具有繼承性,即該事件的父類或與該事件實現同一接口的訂閱者都會處理該事件
        if (eventInheritance) {
            //查找所有可處理該事件相關類或接口
            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
            int countTypes = eventTypes.size();
            for (int h = 0; h < countTypes; h++) {//遍歷所有事件派送到的類
                Class<?> clazz = eventTypes.get(h);
                //檢查這些可處理事件的類是否有處理對應事件的方法,返回值為boolean類型
                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));
            }
        }
    }
//查找所有事件類
private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
        //private static final Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<>();
        synchronized (eventTypesCache) {//同步鎖
            List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
            if (eventTypes == null) {
                eventTypes = new ArrayList<>();
                Class<?> clazz = eventClass;
                //直到clazz為空跳出循環
                while (clazz != null) {//查找派送事件的所有父類以及實現同一接口的類
                    eventTypes.add(clazz);
                    addInterfaces(eventTypes, clazz.getInterfaces());
                    clazz = clazz.getSuperclass();
                }
                //使用緩存把派送事件類型及其父類或實現同一接口類進行緩存
                eventTypesCache.put(eventClass, eventTypes);
            }
            return eventTypes;
        }
    }
//檢查所有可處理該派送事件的類
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {//根據相關List<?>取值subscriptions
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        //如果訂閱事件處理器不為空,且有值時,進行遍歷,否則返回false
        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) {//如果被取消,則跳出循環,返回false
                    break;
                }
            }
            return true;
        }
        return false;
    }

postSingleEvent方法首先根據發布的事件event來獲取該發布事件的class,然后根據發布事件具有繼承性,查找所有可消費處理該事件的相關類(訂閱者)來處理該事件,過程中又回到前面已經分析過的postToSubscription方法,根據不同線程,調用不同方法對事件進行派發.至此,post方法也分析完畢.
EventBus#postSticky

public void postSticky(Object event) {
        synchronized (stickyEvents) {
            stickyEvents.put(event.getClass(), event);
        }
        // Should be posted after it is putted, in case the subscriber wants to remove immediately
        post(event);
    }

補充說明下黏性事件,因為黏性事件其實也是調用post方法,只是在post之前先緩存了黏性事件而已.
最后分析一下反注冊unregister方法
EventBus#unregister

public synchronized void unregister(Object subscriber) {
        //private final Map<Object, List<Class<?>>> typesBySubscriber;
        //從集合中取出該訂閱者所有的訂閱事件
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {//如果訂閱事件集合不為空時
            for (Class<?> eventType : subscribedTypes) {//遍歷,反注冊
                unsubscribeByEventType(subscriber, eventType);
            }
            //從集合中移除該訂閱者
            typesBySubscriber.remove(subscriber);
        } else {//為空時不需要反注冊
            Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }
//解除訂閱者與訂閱事件之間的關系
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
        //private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
        //從集合中取出所有關于該訂閱者的訂閱事件處理器
        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;//標志位置為false
                    subscriptions.remove(i);//移除訂閱事件處理器
                    i--;
                    size--;
                }
            }
        }
    }

反注冊的邏輯也是很簡單:首先根據訂閱者從集合中取出該訂閱者的所有訂閱事件,然后在該訂閱事件不為空時,進行遍歷,并逐一進行解除.
OK,至此,EventBus源碼分析完結!
T.T~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380