EventBus 源碼分析

EventBus 是一個在 Android 開發(fā)中使用的發(fā)布/訂閱事件總線框架

EventBus...

  1. 簡化組件之間的通信

    • 解耦事件發(fā)送方和接收方
    • 能夠很好地處理Activities、Fragment和后臺線程
    • 避免復雜和容易出錯的依賴關系和生命周期問題
  2. 使您的代碼更簡單

  3. 體積小(~60k jar)

  4. 在實踐中已經被超過10億次安裝的應用程序證明
    ......(官方的王婆賣瓜自賣自夸)

注冊:

EventBus.getDefault().register(this)
 public void register(Object subscriber) {
        //1.獲取訂閱者的Class對象
        Class<?> subscriberClass = subscriber.getClass();
        // 判斷當前訂閱者是否為匿名類(因為在匿名類中,訂閱注解是不可見的,查找方式必須是反射方式,這個后面會詳細說明)
        boolean forceReflection = subscriberClass.isAnonymousClass();
        //2.通過反射獲取訂閱者中開發(fā)人員定義的處理事件的方法集合
        List<SubscriberMethod> subscriberMethods =
                subscriberMethodFinder.findSubscriberMethods(subscriberClass, forceReflection);
        //3.將獲取的訂閱者和訂閱者中處理事件的方法按照一定的規(guī)則相結合并存儲到EventBus內部
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }

register這個方法主要可以分為三部分。
第一步,獲取訂閱者的Class對象,這個訂閱者通常就是我們寫的Activity或者Fragment等。
第二步,findSubscriberMethods方法是關鍵,通過我們第一步拿到的訂閱者的Class對象,反射獲取所有符合規(guī)則的處理事件的方法。后面單獨看源碼
第三步,將獲取的訂閱者和訂閱者中處理事件的方法按照一定的規(guī)則相結合并存儲到EventBus內部

第一步很簡單,分析第二步的findSubscriberMethods方法:

 List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass, boolean forceReflection) {
        String key = subscriberClass.getName();
        List<SubscriberMethod> subscriberMethods;
        //首先看緩存中是否已經存在該訂閱者的處理方法的集合
        synchronized (METHOD_CACHE) {
            subscriberMethods = METHOD_CACHE.get(key);
        }
        if (subscriberMethods != null) {
            return subscriberMethods;
        }
      
        //查找事件處理方法有索引方式和反射方式兩種
        if (INDEX != null && !forceReflection) {
            //索引方式查找,其實一般默認進入當前 if 邏輯
            subscriberMethods = findSubscriberMethodsWithIndex(subscriberClass);
            if (subscriberMethods.isEmpty()) {
                //索引方式查找不到,依舊通過反射查找
                subscriberMethods = findSubscriberMethodsWithReflection(subscriberClass);
            }
        } else {
            //反射方式查找
            subscriberMethods = findSubscriberMethodsWithReflection(subscriberClass);
        }
       
         //如果當前訂閱者中的處理事件方法集合為空,則拋出異常;反之則加入緩存中并返回
        if (subscriberMethods.isEmpty()) {
            throw new EventBusException("Subscriber " + subscriberClass
                    + " and its super classes have no public methods with the @Subscribe annotation");
        } else {
            synchronized (METHOD_CACHE) {
                //加入緩存
                METHOD_CACHE.put(key, subscriberMethods);
            }
            return subscriberMethods;
        }
    }

索引是什么,我們平常使用EventBus的時候從來都沒用過這個東西,其實在EventBus3.0之前是沒有索引這個東西的,第二步獲取方法集合走的是 findSubscriberMethodsWithReflection(subscriberClass); 這個方法。那為什么在3.0之后加上了索引呢?其實答案很簡單,就是因為反射獲取方法集合是比較低效的,使用反射所耗費的時間開銷比較大。如果使用索引,EventBus的性能會提升一倍。具體如何在你的代碼中使用EventBus的索引,可以參考 [greenrobot官網中index的介紹] ,實際應用中,我們使用EventBus根本沒添加過索引相關的配置,所以最終我們還是走反射邏輯(http://greenrobot.org/eventbus/documentation/subscriber-index/) 。

既然通過反射查找,接著看findSubscriberMethodsWithReflection方法

 private List<SubscriberMethod> findSubscriberMethodsWithReflection(Class<?> subscriberClass) {
        List<SubscriberMethod> subscriberMethods = new ArrayList<SubscriberMethod>();
        Class<?> clazz = subscriberClass;
        HashSet<String> eventTypesFound = new HashSet<String>();
        StringBuilder methodKeyBuilder = new StringBuilder();
        while (clazz != null) {
            // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                int modifiers = method.getModifiers();
                //方法為public,且沒有final,static等修飾符
                if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                    Class<?>[] parameterTypes = method.getParameterTypes();
                    //方法只能有一個參數(shù)
                    if (parameterTypes.length == 1) {
                        Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                        if (subscribeAnnotation != null) {
                            String methodName = method.getName();
                            Class<?> eventType = parameterTypes[0];
                            methodKeyBuilder.setLength(0);
                            methodKeyBuilder.append(methodName);
                            methodKeyBuilder.append('>').append(eventType.getName());

                            String methodKey = methodKeyBuilder.toString();
                            if (eventTypesFound.add(methodKey)) {
                                // Only add if not already found in a sub class
                                ThreadMode threadMode = subscribeAnnotation.threadMode();
                                subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                        subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                            }
                        }
                    } 
                } 
            }

            clazz = clazz.getSuperclass();
        }
        return subscriberMethods;
    }

該方法很明確,就是通過反射獲取訂閱者所有方法集合后,遍歷所有方法,查找符合規(guī)則的方法 ,而規(guī)則無非就是這些方法必須加 @subscribe 注解,參數(shù)必須有且只有一個等等

進入第三步,遍歷第二步獲取的方法集合,調用 subscribe(subscriber, subscriberMethod)

for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }

subscribe(subscriber, subscriberMethod)是最后一步的核心代碼

 private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        //獲取方法中的事件類型
        Class<?> eventType = subscriberMethod.eventType;
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        //將訂閱者和方法綁在一起形成一個Subscription對象
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<Subscription>();
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }

        //按照priority優(yōu)先級,將上面由訂閱者和方法綁在一起形成的Subscription對象加入subscriptions集合中
        synchronized (subscriptions) {
            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<Class<?>>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);


        //如果方法可以接收sticky事件
        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);
            }
        }
    }

這段代碼雖然比較多,但看下來,功能就是初始化EventBus這個單例的的全局變量,主要是subscriptionsByEventType,typesBySubscriber, 通過名字我們也可以知道,都是map。
其中,subscriptionsByEventType 的key存的是eventType,value存的是Subscription的集合,記錄了每一個相同的事件類型所關聯(lián)的一切注冊者和他們的處理事件的方法。
typesBySubscriber的key是subscriber訂閱者,value是eventType的集合,它記錄了每一個訂閱者所注冊(care)的一切事件類型。

subscribe方法最后對sticky粘性事件做了處理,再次貼下后面的代碼

if (subscriberMethod.sticky) {
            if (eventInheritance) {
               //遍歷stickEvents這個map
                Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
                for (Map.Entry<Class<?>, Object> entry : entries) {
                    Class<?> candidateEventType = entry.getKey();
                    //如果找到了跟這個方法的eventType一致或是其子類的stickEvent
                    if (eventType.isAssignableFrom(candidateEventType)) {
                        Object stickyEvent = entry.getValue();
                        //發(fā)送事件,本質就是通過反射直接調用這個方法 
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
                Object stickyEvent = stickyEvents.get(eventType);
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }

什么是粘性事件?
簡單講,就是在發(fā)送事件之后再訂閱該事件也能收到該事件。Android中就有這樣的實例,也就是Sticky Broadcast,即粘性廣播。正常情況下如果發(fā)送者發(fā)送了某個廣播,而接收者在這個廣播發(fā)送后才注冊自己的Receiver,這時接收者便無法接收到 剛才的廣播,為此Android引入了StickyBroadcast,在廣播發(fā)送結束后會保存剛剛發(fā)送的廣播(Intent),這樣當接收者注冊完 Receiver后就可以接收到剛才已經發(fā)布的廣播。這就使得我們可以預先處理一些事件,讓有消費者時再把這些事件投遞給消費者.

EventBus也提供了這樣的功能,有所不同是EventBus會存儲所有的Sticky事件,如果某個事件在不需要再存儲則需要手動進行移除。用戶通過Sticky的形式發(fā)布事件,而消費者也需要通過Sticky的形式進行注冊,當然這種注冊除了可以接收 Sticky事件之外和常規(guī)的注冊功能是一樣的,其他類型的事件也會被正常處理。

繼續(xù)回到這段代碼,首先判斷了處理事件的方法是否有stick這個注解,如果存在則進入下一步。eventInheritance這個變量默認為true,代表事件是否具有繼承性。可以這么理解,發(fā)送一個事件或者發(fā)送這個事件類型的子類,EventBus的處理事件方法中的參數(shù)類型是父類型,那么這個方法既可以收到父類型的事件,也可以收到子類型的事件。那么如果處理事件方法中的參數(shù)類型是子類型呢?那這個方法就只能接收子類型的事件,而無法接收父類型的事件,這點是在if (eventType.isAssignableFrom(candidateEventType)) 這一行代碼做的判斷。isAssignableFrom這個方法是JDK中Class類中的一個方法,作用就是判斷前者是否是與后者類型一致或前者是后者的父類。下面這個代碼就很簡單了checkPostStickyEventToSubscription(newSubscription, stickyEvent);發(fā)送事件,我們跟進去看一下:

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, Looper.getMainLooper() == Looper.myLooper());
        }
    }

這個方法的功能很簡單,對事件做了非空校驗,然后調用了postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper()) 方法,這個方法內部就是通過反射調用method.invoke(subscription.subscriber, event)來執(zhí)行我們定義的對事件的處理方法。這個方法很重要,后續(xù)在post方法的時候著重分析,現(xiàn)在需要知道它的功能即可??吹竭@里,也就能明白,為什么在粘性事件發(fā)出后再注冊的事件處理方法也可以接收到它了,因為之前發(fā)送的stickyEvent都會存入stickyEvents這個map類型的EventBus中的成員變量中,當我們注冊調用register方法時,在register方法內部會直接通過checkPostStickyEventToSubscription(newSubscription, stickyEvent);來執(zhí)行我們定義的粘性事件處理方法。

至此,終于說完了register這個方法,這個方法的內容看似只有短短幾行,但它所做的事情還是很繁瑣復雜的。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • EventBus源碼分析(一) EventBus官方介紹為一個為Android系統(tǒng)優(yōu)化的事件訂閱總線,它不僅可以很...
    蕉下孤客閱讀 4,091評論 4 42
  • EventBus源碼分析 Android開發(fā)中我們最常用到的可以說就是EventBus了,今天我們來深入研究一下E...
    BlackFlag閱讀 543評論 3 4
  • EventBus是在Android中使用到的發(fā)布-訂閱事件總線框架,基于觀察者模式,將事件的發(fā)送者和接收者解耦,簡...
    BrotherTree閱讀 460評論 0 1
  • 前面對EventBus 的簡單實用寫了一篇,相信大家都會使用,如果使用的還不熟,或者不夠6,可以花2分鐘瞄一眼:h...
    gogoingmonkey閱讀 356評論 0 0
  • 面試有一種技巧據(jù)說叫做反客為主,當遇到Activity-Fragment通信,甚至模塊化開發(fā)時的通信問題等等,可以...
    TruthKeeper閱讀 594評論 0 6

友情鏈接更多精彩內容