EventBus源碼分析

面試有一種技巧據說叫做反客為主,當遇到Activity-Fragment通信,甚至模塊化開發時的通信問題等等,可以將TA引導到你擅長的領域中來,比如說用EventBus來解決,(RxBus也可以)這時一般套路都會問你懂不懂EventBus的原理,這時你就可以以下文的姿勢這樣回(zhuang)答(b)了
(??????)??

簡介

今天我們來分析截止到現在的3.0.0最新版本
慣例介紹一下我們今天的主角,EventBus,專門管理Android事件通訊。

//依賴
dependencies {
compile 'org.greenrobot:eventbus:3.0.0'
}
Github示例圖
Github示例圖

在Android開發中難免會遇到以下場景:

  • Activity之間的回調startActivityForResult
  • 某個頁面刷新后聯動其他頁面也刷新
  • Fragment之間的通信
  • more

按筆者之前的尿性就是復雜場景用本地廣播做發送與監聽,廣播大吼一聲,我叫你一聲你敢答應嗎(??????)??,是不是很有代入感,直到后來發現廣播的性能與Intent傳遞數據大小有限制以及相對難以維護后來吃下了EventBus這發安利。
筆者先簡單的寫個基本用法


需要接收事件的類中注冊監聽(不局限Activity)
EventBus.getDefault().register(this);
別忘記在銷毀時反注冊
EventBus.getDefault().unregister(this);
發送事件
EventBus.getDefault().post(Object event);
在需要接收事件的類中隨便寫個方法,例如

 //別忘了這個注解
 @Subscribe
 public void onEventReceive(String event) {

 }

這個方法只要一個參數就夠了,由于我們發送事件的時候EventBus根據類型來做校驗,例如post("test"),此時所有注冊的類中帶有Subscribe注解的方法中只要第一個參數是String類型,那么就會調用這個方法,你問我怎么知道的,我等會再告訴你(__)

淺析

在EventBus光鮮的外表下我們好像看到了觀察者模式的影子,沒錯,我們來從TA的入口開始EventBus.getDefault().register(this);中挨個的調用順序

public class EventBus {
  //暫時省略一些代碼
  private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
  public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }
    public EventBus() {
        this(DEFAULT_BUILDER);
    }
}

一個常見的DCL單例模式+構建者模式,我們再來看看EventBusBuilder的構造器

public class EventBusBuilder {
        //默認的線程池
        private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
        //是否打印出訂閱時拋出異常的日志
        boolean logSubscriberExceptions = true;
        //是否打印出沒有訂閱者異常的日志
        boolean logNoSubscriberMessages = true;
        //當訂閱者訂閱方法執行拋出異常時 ,是否讓EventBus發送一個特定事件
        boolean sendSubscriberExceptionEvent = true;
        //當事件沒有訂閱者時,是否讓EventBus發送一個特定事件
        boolean sendNoSubscriberEvent = true;
        //是否拋出訂閱者的異常
        boolean throwSubscriberException;
        //是否允許事件可以有繼承
        boolean eventInheritance = true;
        //是否忽視生成的索引
        boolean ignoreGeneratedIndex;
        //是否方法嚴格校驗
        boolean strictMethodVerification;
        //線程池
        ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;
        //某些類跳過驗證,類似綠色通道
        List<Class<?>> skipMethodVerificationForClasses;
        //訂閱者的索引集合
        List<SubscriberInfoIndex> subscriberInfoIndexes;

        EventBusBuilder() {
        }
    }

貼心的我又都給你們打上注釋了,一下子看不明白沒關系,后面我們還會再見面的

 //EventBus的一參構造器
 EventBus(EventBusBuilder builder) {
        //根據消息類型的訂閱者們,key對應消息的類型,value對應訂閱這個類型消息的訂閱者們
        subscriptionsByEventType = new HashMap<Class<?>, CopyOnWriteArrayList<Subscription>>();
        //根據訂閱者的類型們,key對應訂閱者,value對應在這個訂閱者訂閱的事件類型們
        typesBySubscriber = new HashMap<Object, List<Class<?>>>();
        //粘性事件,key對應事件類型,valve對應訂閱者
        stickyEvents = new ConcurrentHashMap<Class<?>, Object>();
        //主線程的發送器,本質是Handler
        mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
        //后臺線程的發送器,本質是Runnable
        backgroundPoster = new BackgroundPoster(this);
        //異步線程的發送器,本質是Runnable
        asyncPoster = new AsyncPoster(this);
        //訂閱者索引數量
        indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
        //訂閱者方法搜尋器
        subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
                builder.strictMethodVerification, builder.ignoreGeneratedIndex);
        //↓接受builder的變量
        logSubscriberExceptions = builder.logSubscriberExceptions;
        logNoSubscriberMessages = builder.logNoSubscriberMessages;
        sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
        sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
        throwSubscriberException = builder.throwSubscriberException;
        eventInheritance = builder.eventInheritance;
        executorService = builder.executorService;
    }

如果看官稍微留意一點注釋,相信已經對EventBus有所猜測了,沒錯,在register方法中EventBus肯定對訂閱者進行了不為人知的操作,將訂閱者和接受的事件等等統統做了記錄,抱著這樣的猜測我們來瞧瞧register

  public void register(Object subscriber) {
        //獲取class
        Class<?> subscriberClass = subscriber.getClass();
        //搜尋器去尋找訂閱者要訂閱的的集合
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            //同步遍歷后一一訂閱
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

這里可以這樣打個比方,subscriber比作小明小明想訂報紙,他去找EventBus,說我要訂報紙,然后EventBus派遣搜尋器小剛去整理你要的類型,一看你原來要訂旅游(String),拍照(Integer)和運動(Boolean),然后就去專門的專欄一一訂閱,以后這些專欄一更新,EventBus就把報紙送到你的手上。
這里我們來看看subscriberMethodFinder的初始化,初始化就在上文EventBus的構造器中

 SubscriberMethodFinder(List<SubscriberInfoIndex> subscriberInfoIndexes, boolean strictMethodVerification,
                           boolean ignoreGeneratedIndex) {
        this.subscriberInfoIndexes = subscriberInfoIndexes;
        this.strictMethodVerification = strictMethodVerification;
        this.ignoreGeneratedIndex = ignoreGeneratedIndex;
    }

將訂閱者的索引集合,是否方法嚴格校驗,是否忽視索引3個參數傳入,有關索引做這塊先賣個關子,先提前劇透一下,今天是小明要訂報紙,明天又來個小張,小剛每次拿筆都要記一下再去專欄訂閱,小張覺得太累了,于是想了個法子,每次要來訂報紙,你們先填張表,填完給我,這樣將就省事多了。

深入

接下來我們來看看搜尋器的尋找方法有沒有什么貓膩

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

同理我們先跳過索引,先來分析findUsingReflection方法

private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
        //保存記錄
        FindState findState = prepareFindState();
        //初始化
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            //通過反射來獲取訂閱方法
            findUsingReflectionInSingleClass(findState);
            //移動到父類
            findState.moveToSuperclass();
        }
        //將最后結果返回并回收資源
        return getMethodsAndRelease(findState);
    }

FindState就在SubscriberMethodFinder下作為靜態內部類,具體如下

static class FindState {
        //尋找到的訂閱方法
        final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
        //所有符合事件類型的方法,包含父類的
        //key對應事件類型,value對應方法Method
        final Map<Class, Object> anyMethodByEventType = new HashMap<>();
        //訂閱者可接受的
        //key對應字符串Method名稱+">"+事件類型名稱,value對應Method的聲明類
        final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
        //拼接
        final StringBuilder methodKeyBuilder = new StringBuilder(128);
        //訂閱者的類型
        Class<?> subscriberClass;
        //記錄的當前的類
        Class<?> clazz;
        //是否跳過父類
        boolean skipSuperClasses;
        //索引
        SubscriberInfo subscriberInfo;

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

        //回收資源
        void recycle() {
            subscriberMethods.clear();
            anyMethodByEventType.clear();
            subscriberClassByMethodKey.clear();
            methodKeyBuilder.setLength(0);
            subscriberClass = null;
            clazz = null;
            skipSuperClasses = false;
            subscriberInfo = null;
        }

        //通過方法和事件類型是否能夠添加到隊列中
        boolean checkAdd(Method method, Class<?> eventType) {
            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();
                    }
                    anyMethodByEventType.put(eventType, this);
                }
                return checkAddWithMethodSignature(method, eventType);
            }
        }

        //通過方法和事件類型是否能夠添加到隊列中,二次校驗
        private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
            methodKeyBuilder.setLength(0);
            methodKeyBuilder.append(method.getName());
            methodKeyBuilder.append('>').append(eventType.getName());
            //按照指定格式拼接作為String key
            String methodKey = methodKeyBuilder.toString();
            Class<?> methodClass = method.getDeclaringClass();
            Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
            if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
                //集合未添加過 || 添加過并且后續添加的訂閱者方法聲明類和之前覆蓋的值有子父關系或者同屬一類
                return true;
            } else {
                //添加過了且沒有類關系
                subscriberClassByMethodKey.put(methodKey, methodClassOld);
                return false;
            }
        }

        //移動到父類
        void moveToSuperclass() {
            if (skipSuperClasses) {
                clazz = null;
            } else {
                clazz = clazz.getSuperclass();
                String clazzName = clazz.getName();
                //跳過系統類
                if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) {
                    clazz = null;
                }
            }
        }
    }

理解起來比較雞肋,沒什么營養,我們結合findUsingReflectionInSingleClass一起來看

 private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
        try {
            //獲取當前類中的聲明方法集合
            //原文注釋說比下面的效率高
            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();
            //方法只能是public,并且不能為static,abstract和2個筆者不懂的迷之修飾符_(:з」∠)_
            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)) {
                            //提取注解的線程修飾mode
                            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");
            }
        }
    }

仔細一看就明白多了,在findUsingReflection方法中先初始化了一個FindState對象,然后對訂閱者類型的父子繼承關系以及事件類型的匹配進行層層篩選,將篩選結果作為List<SubscriberMethod>返回給上級,篩選過程比較復雜,感興趣的同學可以參照筆者上面的注釋仔細品味。
總結如下:

  • 訂閱的方法需要@Subscribe修飾,里面可以附帶線程模式和和優先級;
  • 訂閱方法必須為public,不能為abstract和static;
  • 允許訂閱者之間的繼承關系;

其中SubscriberMethod內部存放一些事件的信息,我們一會再提

public class SubscriberMethod {
    final Method method;
    final ThreadMode threadMode;
    final Class<?> eventType;
    final int priority;
    final boolean sticky;
    /** Used for efficient comparison */
    String methodString;
}

在將結果返回的方法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;
    }

好了,萬里長征已經進行了一半了,什么!?你和我說才一半
(╯‵□′)╯︵┻━┻。。。堅持一下,馬上就革命勝利了
┬─┬ ノ( ' - 'ノ)
我們回到EventBus的register方法,下一步是加鎖遍歷搜尋器返回的結果,也就是小剛要把小明,小張的訂閱表去訂閱道指定專欄

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        //獲取事件類型
        Class<?> eventType = subscriberMethod.eventType;
        //一重簡單的封裝
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        //從這個事件類型的map中取出訂閱者們
        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) {
                //消息允許繼承時
                //遍歷map
                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);
            }
        }
    }

subscribe方法中對事件和訂閱者的關系都保存在了map中,然后對粘性事件進行了處理。
這里我們又遇到了粘性事件,打個比方,小張訂閱了音樂頻道的報紙,想在訂閱時希望過往的報紙也能看到。其實和四大組件的粘性廣播相似,又比如我們開啟一個Activity,我們在Activity的onCreate中注冊了事件,但是在傳遞數據時我們的消息已經發出了,而Activity的初始化不是startActivity一下就能開啟的,所以我們在Activity中的訂閱方法要改一改,將注解改成@Subscribe(sticky = true),就能接受到粘性事件。
那么EventBus的注冊方法我們從頭到尾看了一遍,接下來我們來看看分發方法post

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

post方法很簡單,隊列中有消息就發送出去,如何獲取當前線程的狀態原理基于ThreadLocal來實現,這里筆者簡要提一下好了,ThreadLocal是一個線程內部的數據存儲類,通過它可以在指定的線程中存儲一些數據,而這段數據是不與其他線程共享的。內部原理是通過泛型對象數組,在不同的線程會有不同的數組索引值,這樣就可以在不同線程這種調用get 方法時,取到對應線程的數據。
有關PostingThreadState代碼如下

private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
        @Override
        protected PostingThreadState initialValue() {
            return new PostingThreadState();
        }
    };
final static class PostingThreadState {
        final List<Object> eventQueue = new ArrayList<Object>();
        boolean isPosting;
        boolean isMainThread;
        Subscription subscription;
        Object event;
        boolean canceled;
    }

我們繼續順藤摸瓜來看看發送消息的具體實現

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
                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) {
                //EventBus發送一個沒人認領的事件555。。。
                post(new NoSubscriberEvent(this, event));
            }
        }
    }

原來這個方法還不是真身,只是對消息類型進行了繼承關系的判斷,并且獲取到消息發送結果,如果無訂閱者訂閱則發送一個特定事件,那么再來看看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;
    }

果然,EventBus按照這個類型的消息依次發送給訂閱者們,高優先級的訂閱者甚至可以中斷低優先級的訂閱者們,那么我們來看消息是怎么調度給訂閱者的呢

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

我們看到了四種類型

  • POSTING:默認線程環境,直接調用invokeSubscriber方法,表示在post消息的線程環境直接執行訂閱者的訂閱方法
  • MAIN:如果當前在主線程中,那么直接執行,否則就切到主線程去響應事件
  • BACKGROUND:如果當前在工作線程中,那么直接執行,否則就轉到工作線程響應事件
  • ASYNC:不管post線程情況,直接切到一個新線程中處理事件

事件響應線程和優先級0-100都可以在注解中配置

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
    ThreadMode threadMode() default ThreadMode.POSTING;

    boolean sticky() default false;

    int priority() default 0;
}

其中我們先來看看invokeSubscriber

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

果然,直接通過反射直接調用了invoke,那么MAIN模式是怎么切換的呢,還是老樣子,我們從入口來
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);

HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
        super(looper);
        this.eventBus = eventBus;
        this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
        queue = new PendingPostQueue();
    }

可以從命名猜測HandlerPoster內部維護了一個隊列

 //EventBus調用的入口
 void enqueue(Subscription subscription, Object event) {
        //從后續隊列池中獲取一條
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            queue.enqueue(pendingPost);
            if (!handlerActive) {
                handlerActive = true;
                if (!sendMessage(obtainMessage())) {
                    throw new EventBusException("Could not send handler message");
                }
            }
        }
    }

    @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) {
                        //再次校驗,有點像DCL單例。
                        pendingPost = queue.poll();
                        if (pendingPost == null) {
                            handlerActive = false;
                            return;
                        }
                    }
                }
                //反射執行
                eventBus.invokeSubscriber(pendingPost);
                long timeInMethod = SystemClock.uptimeMillis() - started;
                if (timeInMethod >= maxMillisInsideHandleMessage) {
                    //保證2條EventBus之間處理消息間隔不小于10s,防止主線程卡頓
                    if (!sendMessage(obtainMessage())) {
                        throw new EventBusException("Could not send handler message");
                    }
                    rescheduled = true;
                    return;
                }
            }
        } finally {
            handlerActive = rescheduled;
        }
    }

看來最后涉及線程間調度后處理消息還有貓膩,我們在PendingPost里瞧瞧看

final class PendingPost {
    //待處理的  事件對應訂閱者的 請求池 
    private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();

    Object event;
    Subscription subscription;
    PendingPost next;

    private PendingPost(Object event, Subscription subscription) {
        this.event = event;
        this.subscription = subscription;
    }
    //
    static PendingPost obtainPendingPost(Subscription subscription, Object event) {
        synchronized (pendingPostPool) {
            int size = pendingPostPool.size();
            if (size > 0) {
                //同步在池中取出一個對象池
                PendingPost pendingPost = pendingPostPool.remove(size - 1);
                pendingPost.event = event;
                pendingPost.subscription = subscription;
                pendingPost.next = null;
                return pendingPost;
            }
        }
        return new PendingPost(event, subscription);
    }

    static void releasePendingPost(PendingPost pendingPost) {
        pendingPost.event = null;
        pendingPost.subscription = null;
        pendingPost.next = null;
        synchronized (pendingPostPool) {
            //池中增長速度不要過快,1000數量的閾值
            if (pendingPostPool.size() < 10000) {
                pendingPostPool.add(pendingPost);
            }
        }
    }

}

這么一看好像就只是個對象池嘛,不知道有沒有分析錯,敬請dalao指正,那么BACKGROUNDASYNC我們也就快速過一遍好了,上文概要的提到這2個mode其實都是實現Runnable接口,只不過BACKGROUND內部的run方法是同步單線程處理的,而ASYNC是相當于多線程的。
那么Eventbus的post事件我們已經了解完了原理,無非就是遍歷所有該處理這個事件的訂閱者,然后根據方法的注解來分配到指定的線程環境中invoke執行,那么發送粘性事件是在怎么樣的呢

  public void postSticky(Object event) {
        synchronized (stickyEvents) {
            stickyEvents.put(event.getClass(), event);
        }
        //也是調用post
        post(event);
    }

其實本質發送粘性事件和普通發送是一個原理,只不過追加了這條事件到粘性隊列中,而后來訂閱者如果要響應之前發的粘性事件那么就應該是在訂閱的時候,就是在subscribe方法下的checkPostStickyEventToSubscription,方法內部直接調用postToSubscription。
那么EventBus的一套正常流程我們就基本分析完了(°?°)?其余還有一些EventBus的取消后續事件cancelEventDelivery,移除粘性事件removeStickyEvent,反注冊unregister,都比較簡單,這里就不分析了。

Plus

你們以為這就把EventBus源碼分析完了,當然沒有!EventBus還有一個必殺技我們還沒分析,綜上所述,EventBus對訂閱者信息多出用到了反射,這是在一定程度上消耗性能的,那么EventBus的開源作者是怎么優化的呢,沒錯就是通過預編譯技術來生成索引,相像一下,我們每次要查詢一個單詞就要從頭去字典找,如果現在有個字典的目錄,我們不是就能很快的找到嗎?



這張據說是開源作者博客里的性能對比圖,可以看到加了索引的EventBus性能簡直就是打了雞血,蹭蹭蹭的上去。
完整依賴如下
在項目根目錄gradle中導入apt編譯插件

dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}

Moudle處

apply plugin: 'com.neenbedankt.android-apt'
apt {
    arguments {
        //改成你的索引
        eventBusIndex "com.tk.test.EventBusIndex"
    }
}
dependencies {
        compile 'org.greenrobot:eventbus:3.0.0'
        apt 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}

編譯過后我們會在這個路徑下找到索引app>build>generated>source>apt>debug>com.tk.test.EventBusIndex

/** This class is generated by EventBus, do not edit. */
public class EventBusIndex implements SubscriberInfoIndex {
    private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;

    static {
        SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();

        putIndex(new SimpleSubscriberInfo(com.tk.test.activity.SplashActivity.class, true, new SubscriberMethodInfo[] {
            //通過apt的方式直接把方法名等等參數全放這里了
            new SubscriberMethodInfo("onEventReceive", String.class, ThreadMode.POSTING, 0, true),
        }));
 
    }

    private static void putIndex(SubscriberInfo info) {
        SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
    }
     //實現方法,用來將指定訂閱者類型的相關信息提取出來
    @Override
    public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
        SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
        if (info != null) {
            return info;
        } else {
            return null;
        }
    }
}
//初始化導入索引
EventBus.builder()
                .addIndex(new EventBusIndex())
                .installDefaultEventBus();

EventBusIndex只有一條記錄,因為筆者只有一個Activity作為訂閱者╮(╯▽╰)╭,那么索引又是在哪里被讀取到EventBus的map中呢,我們再回過頭來看看搜尋器的findSubscriberMethods方法,在里面有一個從索引中尋找的方法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);
    }

private SubscriberInfo getSubscriberInfo(SubscriberMethodFinder.FindState findState) {
        if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
            //訂閱者有繼承關系
            SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
            if (findState.clazz == superclassInfo.getSubscriberClass()) {
                return superclassInfo;
            }
        }
        if (subscriberInfoIndexes != null) {
            for (SubscriberInfoIndex index : subscriberInfoIndexes) {
                //遍歷索引
                SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
                if (info != null) {
                    return info;
                }
            }
        }
        return null;
    }

其實基本和這個方法findUsingReflection雷同,然后從FindState的subscriberInfo或者builder中的索引集合中取出來,然后遍歷索引再進行篩選。后面的流程就一樣了
apt筆者不太懂,這里就拋磚引玉了,大抵就是編譯時把之前的搜尋器SubscriberMethodFinder的掃描過程以另外一種模式又執行了一遍,然后將結果生成了一個類,原理和大名鼎鼎的ButterKnife類似。

Ps:EventBus在開發中可以封裝成一個統一的抽象Event或者接口,訂閱者們通過消息的key來區分;
如果對RxJava了解比較深入的同學可以用RxBus來代替,能節省一些代碼

這幾天學習源碼的過程有點累,文章如有錯誤,敬請指正!
最后再上一張筆者整理的流程圖

耶穌說:“我就是道路、真理、生命;若不藉著我,沒有人能到父那里去。 (約翰福音 14:6 和合本)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容