Android EventBus源碼解析

EventBus使用

這里推薦一個博客吊圾,講的很流暢~戳這里

源碼解析

首先顶捷, 我們來看一下獲取EventBus對象的方法:

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

這是一個靜態(tài)方法挂绰,在此處的實現(xiàn)是一種很巧妙的單例模式,既保證了效率服赎,又保證了線程安全葵蒂。不知道的同學(xué)可以自己查一下相關(guān)的資料。

今天我們先從將事件發(fā)出分析重虑,也就是post方法践付。

    /** Posts the given event to the event bus. */
    public void post(Object event) {
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        eventQueue.add(event);

        if (!postingState.isPosting) {
            postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
            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;
            }
        }
    }

    private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
        @Override
        protected PostingThreadState initialValue() {
            return new PostingThreadState();
        }
    };

可以看到,ThreadLocal能夠根據(jù)當(dāng)前所處的線程缺厉,選出該線程中存儲的對應(yīng)對象荔仁。所以當(dāng)在不同線程發(fā)出Event時,Event會被加入到不同的隊列中芽死。
Looper.myLooper方法會返回當(dāng)前線程對應(yīng)的Looper乏梁,Looper.getMainLooper方法會返回主線程對應(yīng)的Looper,根據(jù)兩個兩個方法的返回結(jié)果是否相同关贵,能夠知道發(fā)出post的線程是否為主線程遇骑。接著就進入了一個循環(huán)中,可以猜測出揖曾,每次循環(huán)應(yīng)該會發(fā)出一個Event并處理落萎,直到該線程的所有Event都被處理完。跟進postSingleEvent方法:

    private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        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 {
            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));
            }
        }
    }

這里調(diào)用了lookupAllEventTypes方法炭剪,它又是干什么的呢练链?

    /** Looks up all Class objects including super classes and interfaces. Should also work for interfaces. */
    private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
        synchronized (eventTypesCache) {
            List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
            if (eventTypes == null) {
                eventTypes = new ArrayList<>();
                Class<?> clazz = eventClass;
                while (clazz != null) {
                    eventTypes.add(clazz);
                    addInterfaces(eventTypes, clazz.getInterfaces());
                    clazz = clazz.getSuperclass();
                }
                eventTypesCache.put(eventClass, eventTypes);
            }
            return eventTypes;
        }
    }

看到這大家應(yīng)該明白了,這個方法也就是找出Event的所有父類奴拦,比如在MainActivity注冊了父類A媒鼓,那么當(dāng)發(fā)出A的子類B的Event時,MainActivity應(yīng)該收到該Event。
我們回到postSingleEvent方法绿鸣,在獲得到Event及Event的所有父類并裝入一個列表之后疚沐,對列表中的每一項都調(diào)用了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;
    }

    private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;

不難看出,subscriptionsByEventType應(yīng)該是存放著鍵值對:key:Event對應(yīng)的class 潮模,value:監(jiān)聽該Event的觀察者亮蛔。請大家記住這個成員對象,一會我們將會去追蹤這些鍵值對是什么時候被添加的擎厢。
然后對每一個觀察者調(diào)用了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 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);
        }
    }

然后就根據(jù)監(jiān)聽者的注解中標(biāo)明的線程來對Event進行處理:也就是利用反射究流,使得監(jiān)聽者對該Event對象調(diào)用監(jiān)聽方法《猓看一下invokeSubscirber方法

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

邏輯比較簡單芬探,也就是通過反射,使得觀察者的觀察方法對該Event對象調(diào)用沽损,這樣就實現(xiàn)了觀察者模式。下面我們的任務(wù)就是去找subscriptionsByEventType這個變量里的鍵值對是什么時候被添加進去的循头,顯然我們要從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);
            }
        }
    }

這里先是獲取了訂閱者所在的類卡骂,然后調(diào)用了findSubscriberMethods方法国裳,跟進去看一看:

    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        }

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

private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
這個成員變量是用來做緩存的,之后會繼續(xù)介紹全跨。
ignoreGeneratedIndex默認(rèn)為false缝左,所以我們看一下findUsingInfo方法

    private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
        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 {
                findUsingReflectionInSingleClass(findState);
            }
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }

這里initForSubscriber實際上就只是設(shè)置了三個參數(shù):

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

代碼中有些細節(jié)不是我們關(guān)心的,之后程序會調(diào)用findUsingReflectionInSingleClass方法浓若,那我們?nèi)タ匆幌拢?/p>

    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) {
            // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
            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");
            }
        }
    }

詳細解釋一下這段代碼渺杉。首先解釋為什么使用getDeclaredMethods方法:
getMethods()返回類的所有public方法,其中包含其父類的公用方法。
getDeclaredMethods()返回類的所有方法挪钓,包括public/protected/default/private是越,但不包括其父類的方法。
所以使用getDeclaredMethods方法會更快速碌上。

(modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0

private static final int MODIFIERS_IGNORE = Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC;

由此可知倚评,EventBus注冊事件的方法必須為public,非abstract,非static.
然后對Method調(diào)用了getParameterTypes來獲取參數(shù)馏予,再對Method調(diào)用getAnnotation方法來獲取注解天梧,接下來去看一下checkAdd方法:

    boolean checkAdd(Method method, Class<?> eventType) {
        // 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.
        // Usually a subscriber doesn't have methods listening to the same event type.
        Object existing = anyMethodByEventType.put(eventType, method);
        if (existing == null) {
            return true;
        } else {
            if (existing instanceof Method) {
                if (!checkAddWithMethodSignature((Method) existing, eventType)) {
                    // Paranoia check
                    throw new IllegalStateException();
                }
                // Put any non-Method object to "consume" the existing Method
                anyMethodByEventType.put(eventType, this);
            }
            return checkAddWithMethodSignature(method, eventType);
        }
    }

這個方法里做的事就是:根據(jù)Event來保存該觀察者方法,這樣當(dāng)Event到達的時候霞丧,就能夠調(diào)用相應(yīng)的方法呢岗。如果該觀察者方法已經(jīng)被添加過,那么當(dāng)對map調(diào)用put方法時,就會返回該觀察者方法敷燎。
然后繼續(xù)看findUsingReflectionInSingleClass方法暂筝,可以看到將SubscriberMethod方法加入到了隊列中。這樣findSubscriberMethods方法就結(jié)束了硬贯,繼續(xù)看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);
            }
        }
    }

隨后對剛才檢索到的所有觀察者方法調(diào)用了subscribe方法:

    // Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class<?> eventType = subscriberMethod.eventType;
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        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();
        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);

        if (subscriberMethod.sticky) {
            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();
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
                Object stickyEvent = stickyEvents.get(eventType);
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    }

在這里我們終于找到了subscriptionsByEventType!可以看到,監(jiān)聽者和監(jiān)聽方法被組織成了Subscription對象饭豹,鍵值是Event鸵赖。所以當(dāng)Event到達時,就可以通過subscription來找到觀察者和觀察方法拄衰,然后利用反射就可以調(diào)用該方法它褪。

到這里流程就分析完了,對我來說翘悉,更明白了反射和注解的使用方式茫打,希望也對你產(chǎn)生了幫助0.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市妖混,隨后出現(xiàn)的幾起案子老赤,更是在濱河造成了極大的恐慌,老刑警劉巖制市,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抬旺,死亡現(xiàn)場離奇詭異,居然都是意外死亡祥楣,警方通過查閱死者的電腦和手機开财,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來误褪,“玉大人责鳍,你說我怎么就攤上這事∈藜洌” “怎么了薇搁?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長渡八。 經(jīng)常有香客問我啃洋,道長,這世上最難降的妖魔是什么屎鳍? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任宏娄,我火速辦了婚禮,結(jié)果婚禮上逮壁,老公的妹妹穿的比我還像新娘孵坚。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布卖宠。 她就那樣靜靜地躺著巍杈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪扛伍。 梳的紋絲不亂的頭發(fā)上筷畦,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天,我揣著相機與錄音刺洒,去河邊找鬼鳖宾。 笑死,一個胖子當(dāng)著我的面吹牛逆航,可吹牛的內(nèi)容都是我干的鼎文。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼因俐,長吁一口氣:“原來是場噩夢啊……” “哼拇惋!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起抹剩,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤撑帖,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后吧兔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體磷仰,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡袍嬉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年境蔼,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伺通。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡箍土,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出罐监,到底是詐尸還是另有隱情吴藻,我是刑警寧澤,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布弓柱,位于F島的核電站沟堡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏矢空。R本人自食惡果不足惜航罗,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望屁药。 院中可真熱鬧粥血,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至缔御,卻和暖如春抬闷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背刹淌。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工饶氏, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人有勾。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓疹启,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蔼卡。 傳聞我的和親對象是個殘疾皇子喊崖,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,691評論 2 361

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

  • 在《Android EventBus使用》博文中,我們已經(jīng)對EventBus的使用進行了分析雇逞,在這篇博文中荤懂,我們就...
    24K男閱讀 465評論 0 5
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,309評論 25 707
  • Android消息處理機制估計都被寫爛了廊宪,但是依然還是要寫一下,因為Android應(yīng)用程序是通過消息來驅(qū)動的女轿,An...
    一碼立程閱讀 4,474評論 4 36
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理箭启,服務(wù)發(fā)現(xiàn)蛉迹,斷路器傅寡,智...
    卡卡羅2017閱讀 134,711評論 18 139
  • 看了簡書的介紹就進來了,看中了永久留著文字的功能進來了北救,
    常小恰閱讀 273評論 0 0