EventBus源碼解析

前言:作為一名Android開發(fā)人員葡缰,EventBus這個開源庫想必大家在日常的開發(fā)工作中都有用到吧。有關(guān)EventBus的介紹,在這里就略過了泛释。如果你還不了解的話滤愕,參照EventBus官方文檔。記得我第一次接觸到EventBus的時候怜校,就被它的強(qiáng)大深深折服了间影,很好奇它內(nèi)部是怎么工作的。這兩天翻看了下源碼茄茁,做下學(xué)習(xí)筆記魂贬,希望也能幫助到大家。在這里我選取的EventBus版本為3.1.1裙顽,也就是當(dāng)前的最新版本。

我們知道键科,在EventBus中勋颖,如果我們想要訂閱事件的話酸钦,首先我們必須要注冊成為訂閱者:

    protected void onResume() {
        super.onResume();
        EventBus.getDefault().register(this);
    }

我們點進(jìn)去EventBus的getDefault()方法看一下:

  public EventBus() {
        this(DEFAULT_BUILDER);       //EventBus對象在創(chuàng)建的時候使用了建造者模式
    }

接著跟進(jìn)去:

EventBus(EventBusBuilder builder) {
        logger = builder.getLogger();

        //1.subscriptionsByEventType 是一個hashmap徒恋,以事件類型為key入挣,以訂閱者事件集合為value径筏,代表的是事件類型與其訂閱者事件之間的對應(yīng)關(guān)系。
        //每個事件類型恢氯,可能對應(yīng)多個訂閱者事件勋磕,所以value值為訂閱者事件集合挂滓。
        subscriptionsByEventType = new HashMap<>();

        //2.typesBySubscriber 是一個hashmap,以訂閱者為key吓揪,以事件類型集合為value团秽,代表的是訂閱者與其下事件類型之間的對應(yīng)關(guān)系习勤。
        //每個訂閱者可能會對應(yīng)多個事件類型,所以value值為事件類型集合予颤。
        typesBySubscriber = new HashMap<>();

        //3.粘性事件
        stickyEvents = new ConcurrentHashMap<>();

        mainThreadSupport = builder.getMainThreadSupport();

        //4.mainThreadPoster 切換至主線程時使用
        mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;

        //5.backgroundPoster 切換至后臺線程時使用
        backgroundPoster = new BackgroundPoster(this);

        //6.獨立開辟新線程時使用
        asyncPoster = new AsyncPoster(this);
        indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
        subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
                builder.strictMethodVerification, builder.ignoreGeneratedIndex);
        logSubscriberExceptions = builder.logSubscriberExceptions;
        logNoSubscriberMessages = builder.logNoSubscriberMessages;
        sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
        sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
        throwSubscriberException = builder.throwSubscriberException;
        eventInheritance = builder.eventInheritance;
        executorService = builder.executorService;
    }

EventBus的主要部分就是我在上述代碼中標(biāo)注的六點。大家可以先理解下驳庭。

EventBus的getDefault方法就被我們分析完了饲常,我們了解了EventBus對象的創(chuàng)建過程。接下來就開始解刨register方法了霹娄。我們照例跟進(jìn)去:

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

        // 2      
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);   

        // 3  
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

我們通常會在Activity或者Fragment的onResume方法中完成訂閱操作踩晶,將當(dāng)前Activity對象或者Fragment對象作為參數(shù)傳入渡蜻。那么這個時候當(dāng)前Activity對象或者Fragment對象就作為了訂閱者。

首先在 1 處獲得當(dāng)前訂閱者對應(yīng)的Class對象学密,然后調(diào)用了SubscriberMethodFinder的findSubscriberMethods方法腻暮,將Class對象作為參數(shù)傳入哭靖,來獲取到當(dāng)前訂閱者對應(yīng)的所有訂閱方法。最后將訂閱方法集合進(jìn)行遍歷操作铺坞,分別對每個訂閱方法完成“訂閱”康震。

接下來我們跟進(jìn)去SubscriberMethodFinder(訂閱者方法找尋器)的findSubscriberMethods方法去看一下,看下它是怎么獲取到當(dāng)前訂閱者對應(yīng)的所有訂閱方法的绘梦。

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

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

我先簡單介紹下,METHOD_CACHE為一個HashMap榄棵,以訂閱者對應(yīng)的Class對象為key瘪弓,以訂閱方法集合為value腺怯,用作訂閱方法的緩存虑乖。在 1 處首先從METHOD_CACHE中獲取當(dāng)前訂閱者對應(yīng)的訂閱方法集合决左,如果返回的訂閱方法列表不為null,則直接返回訂閱方法列表继找。因為我們是首次訂閱婴渡,所以subscriberMethods 為null边臼,程序接著往下走。 2 處的ignoreGeneratedIndex在EventBus實現(xiàn)中默認(rèn)為false臼予,會走到else語句粘拾,通過findUsingInfo方法來獲取當(dāng)前訂閱者對應(yīng)的訂閱方法集合缰雇。 3 處會判斷當(dāng)前訂閱者對應(yīng)的訂閱方法集合是否為空盯串,如果為空体捏,會直接拋出異常几缭。如果訂閱方法集合不為空年栓,則將訂閱者Class對象與對應(yīng)的訂閱方法集合存儲到METHOD_CACHE緩存中某抓。

下面我們針對 3 處拋出的異常做個驗證。怎么樣才會讓EventBus拋出這個異常呢备禀?根據(jù)我們剛才的分析曲尸,那就是使當(dāng)前訂閱者對應(yīng)的訂閱方法集合為空另患。怎么著才會使它為空呢?很簡單为严,我們在一個Activity中只參與訂閱而沒有對應(yīng)的事件接收方法:

在這里我以MainActivity為例,只在它的onResume方法中完成了訂閱操作应民,而在MainActivity沒有任何事件接收方法繁仁。(當(dāng)然你在onDestroy方法中取不取消訂閱都沒有影響)MainActivity代碼如下:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

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

這個時候我們運(yùn)行下我們的程序:

Caused by: org.greenrobot.eventbus.EventBusException: Subscriber class com.example.administrator.eventbustest.MainActivity and its super classes have no public methods with the @Subscribe annotation
                                                       at org.greenrobot.eventbus.SubscriberMethodFinder.findSubscriberMethods(SubscriberMethodFinder.java:67)
                                                       at org.greenrobot.eventbus.EventBus.register(EventBus.java:140)
                                                       at com.example.administrator.eventbustest.MainActivity.onResume(MainActivity.java:19)
                                                       at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1263)
                                                       at android.app.Activity.performResume(Activity.java:6160)
                                                       at android.app.Activity.performResume(Activity.java:6355)
                                                       at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3053)
                                                       at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3102) 
                                                       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2463)

果然如此稚矿,app部署到手機(jī)上直接一閃而退苛败。對比下異常信息,你會發(fā)現(xiàn)正是我們在 3 處拋出的異常哈哈辆它,異常信息提示為:訂閱者類MainActivity及它的所有父類都沒有使用@Subscribe注解標(biāo)注的public方法甸祭。我們通過這個小實驗發(fā)散開來淋叶,無論是Android的FrameWork層還是第三方開源框架煞檩,是不是我們在了解了其內(nèi)部源碼后會對我們定位一些異常信息有幫助呢斟湃?對!絕對是有幫助的坛缕!

咳咳毙沾,我們接著我們之前的思路左胞,定位到 2 處遍烦,具體怎么通過findUsingInfo方法來獲取當(dāng)前訂閱者對應(yīng)的訂閱方法集合的呢服猪?我們跟進(jìn)去看一下:

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {

        //1. 通過調(diào)用prepareFindState方法來獲取FindState對象
        FindState findState = prepareFindState();

        //2. 對findstate對象進(jìn)行初始化操作
        findState.initForSubscriber(subscriberClass);
        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. 通過反射等操作獲取到訂閱方法
                // 將訂閱方法添加到FindState的subscriberMethods集合中
                findUsingReflectionInSingleClass(findState);
            }

            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }

1 處慨丐,通過調(diào)用prepareFindState方法來獲取FindState對象房揭。是怎么獲取的恬砂?FindState對象又是用來做什么的呢?在這里我簡單說下狱掂,F(xiàn)indState對象就是用來臨時存儲當(dāng)前查找狀態(tài)的趋惨。獲取操作代碼:

// private static final int POOL_SIZE = 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();
    }

在這里我簡單說下,F(xiàn)IND_STATE_POOL它就是一個FindState類型數(shù)組兆沙,數(shù)組大小為4葛圃,用來作為FindState對象的緩存昏鹃。

代碼中從FIND_STATE_POOL的腳標(biāo)為0處開始遍歷操作阅嘶,如果返回的FindState對象不為空抡蛙,就直接將FindState對象return掉,否則調(diào)用new FindState()進(jìn)行新對象創(chuàng)建操作熊昌。

接著看下 2 處:

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

2 處對findstate對象進(jìn)行了初始化操作,將訂閱者Class對象賦值給成員變量subscriberClass和clazz昂利。

接著讓我們來到 3 處,看下具體怎么操作的:

private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
        try {

            // 1. 我們剛才對findState.clazz進(jìn)行了賦值操作窝撵,也就是我們的訂閱者Class對象
            //通過反射獲取到該訂閱者中所有聲明的方法
            methods = findState.clazz.getDeclaredMethods();
        } catch (Throwable th) {
            // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
            methods = findState.clazz.getMethods();
            findState.skipSuperClasses = true;
        }

        //遍歷methods方法數(shù)組
        for (Method method : methods) {
           
            // 獲取方法修飾符并判斷是不是PUBLIC修飾或者默認(rèn)修飾符
            int modifiers = method.getModifiers();
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {

                //獲得方法的參數(shù)類型寒砖,判斷參數(shù)個數(shù)是否為1
                Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length == 1) {

                    //獲得方法Subscribe類型注解并判斷是否為null
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) {
                       
                        //獲取參數(shù)類型哩都,也就是EventBus訂閱方法事件類型
                        Class<?> eventType = parameterTypes[0];
                        if (findState.checkAdd(method, eventType)) {
                            //獲取到訂閱方法所要運(yùn)行的線程環(huán)境魁兼,與EventBus使用時指定的線程方式對應(yīng)。
                            ThreadMode threadMode = subscribeAnnotation.threadMode();

                            //創(chuàng)建SubscriberMethod對象漠嵌,并添加到subscriberMethods訂閱者方法集合中
                            //SubscriberMethod中封裝了訂閱方法咐汞、事件類型盖呼、線程環(huán)境以及優(yōu)先級等信息
                            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");
            }
        }
    }

我們回到findUsingInfo方法的最后一句return getMethodsAndRelease(findState);

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

getMethodsAndRelease方法所做的操作就是將剛才新創(chuàng)建的findState對象中存儲的信息全部清空掉,然后添加到FIND_STATE_POOL緩存數(shù)組中化撕。最后將訂閱者方法集合返回。

終于又折回到最初的register方法中了:

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

接下來我們跟進(jìn)去subscribe方法憾朴,看下是怎么對每個訂閱方法完成“訂閱”的做祝。

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {

        //1.獲取到訂閱方法對應(yīng)的事件類型
        Class<?> eventType = subscriberMethod.eventType;

        //2.對當(dāng)前訂閱者和訂閱方法進(jìn)一步封裝翻诉,包裝成Subscription對象(訂閱者事件)
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);

        //3.調(diào)用subscriptionsByEventType 的get方法,將事件類型作為key傳入夜焦,獲取到該事件類型對應(yīng)的訂閱者事件集合
        //關(guān)于 subscriptionsByEventType 在EventBus的構(gòu)造方法中已經(jīng)講述過了抹镊,忘記的話可以向上翻看一下
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);

        //4. 如果該事件類型對應(yīng)的訂閱者事件集合為null儡炼,則會新創(chuàng)建一個訂閱者事件集合唬党,并以該事件類型為key蓝纲,以新創(chuàng)建的訂閱者事件集合為value,添加到subscriptionsByEventType中嗅辣。
        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<>();
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
            if (subscriptions.contains(newSubscription)) {

               //當(dāng)我們在一個Activity或者Fragment中,有相同的訂閱方法時(與方法名稱無關(guān)),會拋出該異常阅签。
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }

        //5. 將新創(chuàng)建的訂閱者事件按照優(yōu)先級添加到訂閱者事件集合中
        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;
            }
        }

        //6. 通過typesBySubscriber的get方法碎连,獲取到當(dāng)前訂閱者所對應(yīng)的訂閱事件類型集合
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        //7. 將該事件類型添加到事件類型集合中
        subscribedEvents.add(eventType);

        //8.判斷是否為粘性事件
        if (subscriberMethod.sticky) {
            // eventInheritance默認(rèn)為true,只是起到優(yōu)化作用口四,對后續(xù)調(diào)用沒有影響
            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();

                        //9. 如果是粘性事件稚照,最終會調(diào)用到此方法
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
                Object stickyEvent = stickyEvents.get(eventType);
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    }

下面我們簡單看下 9 處的checkPostStickyEventToSubscription方法斤彼,源碼如下:

private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
        if (stickyEvent != null) {
            // If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
            // --> Strange corner case, which we don't take care of here.
            postToSubscription(newSubscription, stickyEvent, isMainThread());
        }
    }

可以看到checkPostStickyEventToSubscription內(nèi)部最終又是調(diào)用postToSubscription方法實現(xiàn)的。我們使用EventBus的post方法發(fā)送普通事件,最終也是調(diào)用到postToSubscription方法來處理的治筒∶暾可以簡單說粘性事件就是模擬普通事件的post方法來實現(xiàn)的。這也就解釋了粘性事件在發(fā)送之后注冊仍舊可以收到的原因了。

以上就是EventBus的register方法全部實現(xiàn)過程。

下面我們接著看下EventBus的取消訂閱方法约啊,也就是EventBus.getDefault().unregister(this);
我們點進(jìn)去unregister方法里面看下:

public synchronized void unregister(Object subscriber) {
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
            for (Class<?> eventType : subscribedTypes) {
                unsubscribeByEventType(subscriber, eventType);
            }
            typesBySubscriber.remove(subscriber);
        } else {
            logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }

unregister的方法實現(xiàn)很簡單榔组。首先會調(diào)用typesBySubscriber的get方法,獲取到當(dāng)前訂閱者對應(yīng)的事件類型集合。然后對該事件類型集合進(jìn)行判斷医增,如果集合為null忽刽,則直接打印出警告信息扑眉,提示我們“訂閱者”在此之前并沒有訂閱過。如果集合不為null呆抑,則會進(jìn)行兩部操作:第一步品洛,遍歷當(dāng)前訂閱者對應(yīng)的事件類型集合查邢,調(diào)用subscriptionsByEventType的get方法蔗崎,獲取到每個事件類型對應(yīng)的訂閱者事件集合,然后再對每個事件類型對應(yīng)的訂閱者事件集合進(jìn)行遍歷操作扰藕,從訂閱者事件集合中將當(dāng)前訂閱者的訂閱事件移除掉缓苛。第二步,將當(dāng)前訂閱者從typesBySubscriber中移除掉邓深。
下面我貼下unsubscribeByEventType方法的源碼未桥,相信你一看就會理解了。

private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
        List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions != null) {
            int size = subscriptions.size();
            for (int i = 0; i < size; i++) {
                Subscription subscription = subscriptions.get(i);
                if (subscription.subscriber == subscriber) {
                    subscription.active = false;
                    subscriptions.remove(i);
                    i--;
                    size--;
                }
            }
        }
    }

就這樣芥备,EventBus取消訂閱的方法也就分析完畢了冬耿,有沒有很簡單哈哈。

接下來終于到了我們的post發(fā)送事件的方法了:
EventBus.getDefault().post(new MessageEvent("hello"));我們點進(jìn)去post方法去看下:

public void post(Object event) {
        //private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>()
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        eventQueue.add(event);

        if (!postingState.isPosting) {
            postingState.isMainThread = isMainThread();
            postingState.isPosting = true;
            if (postingState.canceled) {
                throw new EventBusException("Internal error. Abort state was not reset");
            }
            try {
                while (!eventQueue.isEmpty()) {
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

首先從currentPostingThreadState中獲取到PostingThreadState這個對象萌壳,currentPostingThreadState是個什么東西亦镶??袱瓮?我在代碼中已經(jīng)標(biāo)注過了缤骨,它就是一個ThreadLocal,關(guān)于ThreadLocal懂讯,相信看過Handler源碼的同志都會熟悉荷憋,對的,簡單理解下褐望,ThreadLocal保證了線程間數(shù)據(jù)的獨立性。然后將event事件添加到事件隊列中串前,接著進(jìn)入到if語句里面瘫里,標(biāo)記當(dāng)前線程是不是主線程。接著進(jìn)入while循環(huán)荡碾,不斷從事件隊列頭部取出事件谨读,調(diào)用postSingleEvent方法,逐個發(fā)送事件坛吁。我們跟進(jìn)去看下:

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
     
        //1. 獲取到當(dāng)前事件對應(yīng)的事件Class
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
 
        //2.無論eventInheritance的值為true 或者 false劳殖,最終都會調(diào)用到postSingleEventForEventType方法铐尚。
        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 {

            //3. 調(diào)用postSingleEventForEventType方法。
            subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
        }
        if (!subscriptionFound) {
            if (logNoSubscriberMessages) {
                logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
            }
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                post(new NoSubscriberEvent(this, event));
            }
        }
    }

我們跟進(jìn)去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;
    }

方法中首先會調(diào)用subscriptionsByEventType的get方法哆姻,獲取到當(dāng)前事件類型對應(yīng)的訂閱者事件集合宣增。接下來對當(dāng)前事件類型對應(yīng)的訂閱者事件集合進(jìn)行判斷,最終又會調(diào)用到postToSubscription方法中矛缨。postToSubscription方法爹脾??箕昭?有沒有一種似曾相識的感覺灵妨?對的,就是我們之前分析的粘性事件最后調(diào)用到的方法哈哈落竹。

我們點進(jìn)去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 {
                    // temporary: technically not correct as poster not decoupled from subscriber
                    invokeSubscriber(subscription, event);
                }
                break;
            case BACKGROUND:
                if (isMainThread) {
                    backgroundPoster.enqueue(subscription, event);
                } else {
                    invokeSubscriber(subscription, event);
                }
                break;
            case ASYNC:
                asyncPoster.enqueue(subscription, event);
                break;
            default:
                throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
        }
    }

在方法中首先對訂閱者事件標(biāo)注的線程進(jìn)行判斷泌霍,這里我們就拿MAIN線程來舉例。如果訂閱者事件標(biāo)注的線程為MAIN述召,就表明我們的訂閱者事件方法體想要在UI線程中執(zhí)行朱转,這個時候會判斷當(dāng)前線程是不是主線程,如果是主線程就直接調(diào)用invokeSubscriber方法進(jìn)行操作桨武,如果不是主線程肋拔,則通過mainThreadPoster進(jìn)行線程切換咯。

我們先看下當(dāng)前線程是主線程的情況呀酸,也就是會直接調(diào)用到invokeSubscriber方法凉蜂。我們跟進(jìn)去看下:

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

what? 性誉? 窿吩?最終竟然還是通過反射來實現(xiàn)方法調(diào)用的哈哈哈哈!

如果當(dāng)前線程不是主線程的時候错览,又進(jìn)行怎么處理的呢纫雁?這里我們看到會調(diào)用mainThreadPoster的enqueue方法來將訂閱者事件和event對象進(jìn)行入隊操作。話說mainThreadPoster是個什么鬼倾哺?轧邪??

public interface MainThreadSupport {

    boolean isMainThread();

    Poster createPoster(EventBus eventBus);

    class AndroidHandlerMainThreadSupport implements MainThreadSupport {

        private final Looper looper;

        public AndroidHandlerMainThreadSupport(Looper looper) {
            this.looper = looper;
        }

        @Override
        public boolean isMainThread() {
            return looper == Looper.myLooper();
        }

        @Override
        public Poster createPoster(EventBus eventBus) {
            return new HandlerPoster(eventBus, looper, 10);
        }
    }

}

這里我只粘貼了部分代碼羞海,我簡單說下忌愚,mainThreadPoster是由MainThreadSupport來創(chuàng)建的,由上述代碼可知却邓,MainThreadSupport是個接口硕糊,它的的實現(xiàn)類為AndroidHandlerMainThreadSupport。mainThreadPoster對象也就是HandlerPoster實例對象。最終也就是調(diào)用到HandlerPoster的enqueue方法來將訂閱者事件和event對象進(jìn)行入隊操作简十。在這里還要提一點檬某,AndroidHandlerMainThreadSupport中的looper對象為主線程looper。

我們接著看下HandlerPoster的enqueue方法:

//1. public class HandlerPoster extends Handler implements Poster {

public void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {

           //2. private final PendingPostQueue queue = new PendingPostQueue();
            queue.enqueue(pendingPost);
            if (!handlerActive) {
                handlerActive = true;
                if (!sendMessage(obtainMessage())) {
                    throw new EventBusException("Could not send handler message");
                }
            }
        }
    }

可以看到最終又是調(diào)用到了PendingPostQueue的enqueue方法進(jìn)行入隊操作螟蝙。在這里我們直接看下HandlerPoster的handleMessage方法恢恼,該方法運(yùn)行在主線程。

   @Override
    public void handleMessage(Message msg) {
        boolean rescheduled = false;
        try {
            long started = SystemClock.uptimeMillis();
            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;
                        }
                    }
                }

                //1.調(diào)用eventBus的invokeSubscriber方法胶逢。
                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;
        }
    }

可以看到厅瞎,最終調(diào)用到了eventBus的invokeSubscriber方法,而invokeSubscriber又調(diào)用到invokeSubscriber方法初坠。同樣還是通過反射來實現(xiàn)方法調(diào)用和簸,只不過多了一個線程切換而已。

關(guān)于EventBus的源碼就解析到這里了碟刺,筆者能力有限锁保,有什么表達(dá)不正確的,還望各位老哥指出半沽,如果有幫助到你們爽柒,還望點顆小心心支持下。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末者填,一起剝皮案震驚了整個濱河市浩村,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌占哟,老刑警劉巖心墅,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異榨乎,居然都是意外死亡怎燥,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門蜜暑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來铐姚,“玉大人,你說我怎么就攤上這事肛捍∫啵” “怎么了?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵拙毫,是天一觀的道長氢橙。 經(jīng)常有香客問我,道長恬偷,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮袍患,結(jié)果婚禮上坦康,老公的妹妹穿的比我還像新娘。我一直安慰自己诡延,他們只是感情好滞欠,可當(dāng)我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著肆良,像睡著了一般筛璧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上惹恃,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天夭谤,我揣著相機(jī)與錄音,去河邊找鬼巫糙。 笑死朗儒,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的参淹。 我是一名探鬼主播醉锄,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼浙值!你這毒婦竟也來了恳不?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤开呐,失蹤者是張志新(化名)和其女友劉穎烟勋,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體负蚊,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡神妹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了家妆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸵荠。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖伤极,靈堂內(nèi)的尸體忽然破棺而出蛹找,到底是詐尸還是另有隱情,我是刑警寧澤哨坪,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布庸疾,位于F島的核電站,受9級特大地震影響当编,放射性物質(zhì)發(fā)生泄漏届慈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望金顿。 院中可真熱鬧臊泌,春花似錦、人聲如沸揍拆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嫂拴。三九已至播揪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間筒狠,已是汗流浹背猪狈。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留窟蓝,地道東北人罪裹。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像运挫,于是被迫代替她去往敵國和親状共。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 博文出處:EventBus源碼解析谁帕,歡迎大家關(guān)注我的博客峡继,謝謝! 0001B 時近年末匈挖,但是也沒閑著碾牌。最近正好在看...
    俞其榮閱讀 1,294評論 1 16
  • 前言 在寫這篇源碼解析到一半時發(fā)現(xiàn)EventBus有很多高級用法是不能直接忽略的,于是回過頭來寫了EventBus...
    CharmingWong閱讀 425評論 1 4
  • 一儡循、注冊主要流程 EventBus的注冊代碼如下: register方法主要做了三件事: 獲取訂閱者類的class...
    andcoder閱讀 418評論 0 2
  • (1)eventbus的使用 為了方便分析EventBus的源碼舶吗,這里我們先整個demo出來,源碼如下: (2)E...
    礪雪凝霜閱讀 877評論 1 4
  • EventBus是一個開源的事件總線項目择膝,項目地址:EventBus EventBus通過注冊監(jiān)聽器和發(fā)布消息的方...
    JasmineBen閱讀 968評論 1 4