EventBus源碼-流程分析

前言

在項(xiàng)目中遇到了有關(guān)EventBus數(shù)據(jù)方面的問題脆侮,于是就參考源碼梳理一下整個(gè)數(shù)據(jù)流程,最后整理輸出雅倒。

簡(jiǎn)介

EventBus是一個(gè)基于觀察者模式的發(fā)布/訂閱事件總線绅络,解決了以往廣播在組件間通信的不安全、耗時(shí)問題狈茉,替代Intent,Handler,BroadCast在Fragment夫椭,Activity,Service氯庆,線程之間傳遞消息蹭秋。

EventBus

優(yōu)缺點(diǎn)

優(yōu)點(diǎn)->簡(jiǎn)化組件之間的通信,實(shí)現(xiàn)解耦使業(yè)務(wù)簡(jiǎn)潔堤撵,高效仁讨,可以自己設(shè)置事件處理的線程以及優(yōu)先級(jí)。
缺點(diǎn)->需要維護(hù)很多事件類实昨。

使用

  1. 定義事件:

    public static class MessageEvent { /* Additional fields if needed */ }
    
  2. 準(zhǔn)備訂閱者: 申明并注解你的訂閱方法, 指定線程模式(可選)

    @Subscribe(threadMode = ThreadMode.MAIN)  
    public void onMessageEvent(MessageEvent event) {/* Do something */};
    

    在使用的類中陪竿,注冊(cè)和解注冊(cè)訂閱者,如Activity/fragment中:

     @Override
     public void onStart() {
         super.onStart();
         EventBus.getDefault().register(this);
     }
    
     @Override
     public void onStop() {
         super.onStop();
         EventBus.getDefault().unregister(this);
     }
    
  3. 發(fā)送事件:

     EventBus.getDefault().post(new MessageEvent());
    

內(nèi)部實(shí)現(xiàn)

1.EventBus.getDefault().register(this);

注冊(cè)給定的訂閱用戶以接收事件。訂閱者對(duì)接收事件不再感興趣后族跛,必須調(diào)用{@link #unregister(Object)}闰挡。

 public void register(Object subscriber) {
      //通過反射獲取訂閱者Class對(duì)象
      Class<?> subscriberClass = subscriber.getClass();
      //1.5
      List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
      synchronized (this) {
          //遍歷訂閱
          for (SubscriberMethod subscriberMethod : subscriberMethods) {
              //2
              subscribe(subscriber, subscriberMethod);
          }
      }
 }

通過Class對(duì)象獲取它的訂閱事件的集合

1.SubscriberMethodFinder#List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass)

    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        //private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
        //這個(gè)Map key為注冊(cè)事件的類(比如某個(gè)Activity),value為承載著事件類(MessageEvent)和處理事件的方法(onMessageEvent(MessageEvent event))
      //先從此處查找 不為空返回 為空 就添加到map中
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        }

        if (ignoreGeneratedIndex) {
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
            //1.1 獲取所有訂閱者的方法列表
            subscriberMethods = findUsingInfo(subscriberClass);
        }
        //不為空 就添加到map中
        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.1 SubscriberMethodFinder#List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass)

    private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        //從FIND_STATE_POOL (private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];  POOL_SIZE->4)
        //中獲取FindState對(duì)象
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            findState.subscriberInfo = getSubscriberInfo(findState);
          //第一次為null
            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 {
                //通過反射獲取訂閱者類中的所有方法
                findUsingReflectionInSingleClass(findState);
            }
            findState.moveToSuperclass();
        }
        //從findState中獲取訂閱者所有方法的集合
        return getMethodsAndRelease(findState);
    }
2.EventBus#subscribe(Object subscriber, SubscriberMethod subscriberMethod)

    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        //獲取事件類型的Class對(duì)象
        Class<?> eventType = subscriberMethod.eventType;
        //將信息封裝到對(duì)象中
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        //根據(jù)事件類型查找對(duì)應(yīng)的CopyOnWriteArrayList
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<>();
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
            //只能注冊(cè)一次
            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }
        
        //根據(jù)優(yōu)先級(jí)將新構(gòu)建的subscription(subscriber, subscriberMethod)添加到CopyOnWriteArrayList中
        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;
            }
        }

        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);
        //如果是sticky事件 調(diào)用checkPostStickyEventToSubscription方法發(fā)送粘性事件
        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);
            }
        }
    }

注冊(cè)部分主要通過通過訂閱者的Class對(duì)象獲取其中的訂閱方法礁哄,將方法和訂閱者綁定长酗,之后又處理了粘性事件。

2. EventBus.getDefault().post(new MessageEvent());
    public void post(Object event) {
        //currentPostingThreadState(是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 {
                //如果事件隊(duì)列中還有事件 就一直發(fā)送
                while (!eventQueue.isEmpty()) {
                    //3
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

    final static class PostingThreadState {
       final List<Object> eventQueue = new ArrayList<>(); // 線程的事件隊(duì)列
        boolean isPosting; //是否正在發(fā)送
        boolean isMainThread; //是否在主線程中發(fā)送
        Subscription subscription; //關(guān)于事件的封裝
        Object event; //事件對(duì)象
        boolean canceled; //是否被取消發(fā)送
    }
3.EventBus#postSingleEvent(Object event, PostingThreadState postingState)
    private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        if (eventInheritance) {
            //查找所有Class對(duì)象桐绒,包括超類和接口
            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
            int countTypes = eventTypes.size();
            for (int h = 0; h < countTypes; h++) {
                Class<?> clazz = eventTypes.get(h);
                //調(diào)用postSingleEventForEventType方法發(fā)送事件
                subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
            }
        } else {
            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));
            }
        }
    }

    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 {
                    //發(fā)送事件
                    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;
    }
  
    //粘性事件會(huì)直接調(diào)用這個(gè)
    //根據(jù)注解上的線程模式在不用的線程調(diào)用
    private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.threadMode) {
            case POSTING://直接反射調(diào)用方法 不切換線程
                invokeSubscriber(subscription, event);
                break;
            case MAIN://在主線程直接反射調(diào)用 
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {        //否則加入到列隊(duì)中到主線程調(diào)用
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case MAIN_ORDERED://與MAIN類似
                if (mainThreadPoster != null) {
                    mainThreadPoster.enqueue(subscription, event);
                } else {
                    invokeSubscriber(subscription, event);
                }
                break;
            case BACKGROUND:
                if (isMainThread) {//在主線程就加入后臺(tái)隊(duì)列執(zhí)行
                    backgroundPoster.enqueue(subscription, event);
                } else {//直接反射調(diào)用
                    invokeSubscriber(subscription, event);
                }
                break;
            case ASYNC://直接在子線程調(diào)用
                asyncPoster.enqueue(subscription, event);
                break;
            default:
                throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
        }
    }

post方法主要將事件加入集合并且集合還有數(shù)據(jù)就一直發(fā)送夺脾,然后獲取所有訂閱該事件類的訂閱者,判斷他們需要運(yùn)行的線程來(lái)決定在哪個(gè)線程運(yùn)行茉继。

3.EventBus.getDefault().postSticky(new MessageEvent());
    //加入到粘性事件列表中供之后判斷
    public void postSticky(Object event) {
        synchronized (stickyEvents) {
            stickyEvents.put(event.getClass(), event);
        }
        //發(fā)送事件
        post(event);
    }

將數(shù)據(jù)加入到粘性事件列表在registe方法中判斷是否post事件

4.EventBus.getDefault().unregister(this);
    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());
        }
    }

總結(jié)

到這里EventBus源碼流程就結(jié)束了咧叭,它的源碼相較于其他來(lái)說還是比較簡(jiǎn)單的,如果文中有不足之處望指出烁竭。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末菲茬,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子派撕,更是在濱河造成了極大的恐慌婉弹,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件终吼,死亡現(xiàn)場(chǎng)離奇詭異镀赌,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)际跪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門商佛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人姆打,你說我怎么就攤上這事良姆。” “怎么了穴肘?”我有些...
    開封第一講書人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵歇盼,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我评抚,道長(zhǎng)豹缀,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任慨代,我火速辦了婚禮邢笙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘侍匙。我一直安慰自己氮惯,他們只是感情好叮雳,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著妇汗,像睡著了一般帘不。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上杨箭,一...
    開封第一講書人閱讀 52,457評(píng)論 1 311
  • 那天寞焙,我揣著相機(jī)與錄音,去河邊找鬼互婿。 笑死捣郊,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的慈参。 我是一名探鬼主播呛牲,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼驮配!你這毒婦竟也來(lái)了娘扩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤僧凤,失蹤者是張志新(化名)和其女友劉穎畜侦,沒想到半個(gè)月后元扔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體躯保,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年澎语,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了途事。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡擅羞,死狀恐怖尸变,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情减俏,我是刑警寧澤召烂,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布雏蛮,位于F島的核電站躬审,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏打瘪。R本人自食惡果不足惜历筝,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一酗昼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧梳猪,春花似錦麻削、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)叠荠。三九已至,卻和暖如春扫责,著一層夾襖步出監(jiān)牢的瞬間蝙叛,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工公给, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留借帘,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓淌铐,卻偏偏與公主長(zhǎng)得像肺然,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子腿准,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360