源碼解析EventBus

EventBus通過訂閱者/發佈者機制使得整個系統的耦合度更加的鬆散,在不使用Handler和AsyncTask的情況下也能實現對界面的更新,在這篇文章中主要對EventBus源碼進行一個簡要分析,如果還沒有接觸過這個開源庫可以參考一下官方給出的實例代碼~

還是從我們平時使用的地方開始說起。在使用時我們的入口都是EventBus類的靜態方法,那麼最常接觸的就是getDefault方法了,來看一下:

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

構造方法採用了單例模式的雙重檢查來保證單例的唯一性,再看一下defaultInstance:

static volatile EventBus defaultInstance;

將實例聲明爲volatile來保證了及時可見性。

在這裏調用了EventBus的構造方法,我們就不再跟進構造方法中了,由於其構造涉及到的參數數量較多,所以在這裏採取了構造者模式,有興趣可以去看一下實現(鏈式)。


看過了getDefault方法,如果看過例子都應該瞭解,我們還需要在初始化時註冊當前的類,並且在銷燬時反註冊掉當前類,涉及到註冊的方法有如下幾個:

 public void register(Object subscriber) {
        register(subscriber, false, 0);
    }

 public void register(Object subscriber, int priority) {
        register(subscriber, false, priority);
    }

public void registerSticky(Object subscriber) {
        register(subscriber, true, 0);
    }

public void registerSticky(Object subscriber, int priority) {
        register(subscriber, true, priority);
    }

這裏的subscriber顯然就是訂閱者類啦,priority是訂閱者是優先級,至於Sticky我們暫時先不去考慮它。這四個方法都調用了同一個方法:

private synchronized void register(Object subscriber, boolean sticky, int priority) {

        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod, sticky, priority);
        }
    }

在這裏調用了subscriberMethodFinder對象的findSubscriberMethod方法,傳入的是當前的訂閱者Class對象。可以看到subscriberMethodFinder是在構造方法中創建出來的,我們直接來看它的findSubscriberMethod方法:

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {

        String key = subscriberClass.getName();
        List<SubscriberMethod> subscriberMethods;

        synchronized (methodCache) {
            subscriberMethods = methodCache.get(key);
        }
        if (subscriberMethods != null) {
            return subscriberMethods;
        }

        subscriberMethods = new ArrayList<SubscriberMethod>();
        Class<?> clazz = subscriberClass;
        HashSet<String> eventTypesFound = new HashSet<String>();

        StringBuilder methodKeyBuilder = new StringBuilder();
        while (clazz != null) {
            String name = clazz.getName();

            if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {
                // Skip system classes, this just degrades performance
                break;
            }

            // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                String methodName = method.getName();

                if (methodName.startsWith(ON_EVENT_METHOD_NAME)) {

                    int modifiers = method.getModifiers();
                    if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {

                        Class<?>[] parameterTypes = method.getParameterTypes();

                        if (parameterTypes.length == 1) {
                            String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length());
                            ThreadMode threadMode;
                            if (modifierString.length() == 0) {
                                threadMode = ThreadMode.PostThread;
                            } else if (modifierString.equals("MainThread")) {
                                threadMode = ThreadMode.MainThread;
                            } else if (modifierString.equals("BackgroundThread")) {
                                threadMode = ThreadMode.BackgroundThread;
                            } else if (modifierString.equals("Async")) {
                                threadMode = ThreadMode.Async;
                            } else {
                                if (skipMethodVerificationForClasses.containsKey(clazz)) {
                                    continue;
                                } else {
                                    throw new EventBusException("Illegal onEvent method, check for typos: " + method);
                                }
                            }
                            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
                                subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));
                            }
                        }
                    } else if (!skipMethodVerificationForClasses.containsKey(clazz)) {
                        Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + clazz + "."
                                + methodName);
                    }
                }
            }

            clazz = clazz.getSuperclass();
        }
        if (subscriberMethods.isEmpty()) {
            throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called "
                    + ON_EVENT_METHOD_NAME);
        } else {

            synchronized (methodCache) {
                methodCache.put(key, subscriberMethods);
            }
            return subscriberMethods;
        }
    }

方法很長但是並不複雜,一點一點來看一下:

首先得到了訂閱者類的全類名,而後以這個全類名爲key查詢methodCach,下面是這個對象的定義:

private static final Map<String, List<SubscriberMethod>> methodCache = new HashMap<String, List<SubscriberMethod>>();

很顯然第一次在緩存中是一定找不到的,我們繼續向下看。

創建一個ArrayList,從對象名字可以看出這是一個訂閱者方法的集合,而後進入while循環,判斷一下當前的訂閱者類是不是一個系統中的提供類,若是的話直接就終止掉循環。

然後獲取到當前類的所有方法,包括私有方法,但是在這裏作者註釋聲明瞭在這個版本中只允許訂閱者onEvent開頭的方法爲public,這點在後文中將會有所體現。

得到方法的參數,當方法的參數不是一個的時候拋出異常。

截取該類中以onEvent開頭的方法名,並且使用ThreadMode來儲存方法的名字。

後面進行了一個判定的操作然後將方法method,保存信息的threadMode和方法參數Class對象封裝成一個SubscriberMethod類的對象並且存放在subscriberMethods集合中。

而後將clazz對象置爲父類重新進行循環。

最後可以看到以當前訂閱者類全名爲key存入到了緩存中,方便下次使用,而後返回subscriberMethods集合也就是訂閱者方法集合。


到這裏findSubscriberMethods就結束了,我們回到主線上繼續看:

循環出了subscriberMethods集合中的每一個方法然後調用subscribe方法,我們進入看一下:

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {

        Class<?> eventType = subscriberMethod.eventType;
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);
        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);
            }
        }

        // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
        // subscriberMethod.method.setAccessible(true);

        int size = subscriptions.size();
        for (int i = 0; i <= size; i++) {
            if (i == size || newSubscription.priority > subscriptions.get(i).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);
        if (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);
            }
        }
    }

首先得到該方法參數的Class對象,然後以這個對象作爲key從subscriptionsByEventType集合中來找出value,還是看一下定義:

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

先簡單的從名字理解一下,以onEvent開頭的方法參數類型爲key的map,保存了所有包含這種參數類型的Subscription集合(Subscription是由當前訂閱者,訂閱者的單獨一個方法和訂閱者的優先級共同確定的)。

在第一次取出來的時候也是一定會取出一個null,這時候將創建這個集合並且將其放入map。

這時候將之前創建出的Subscription類的對象添加到集合當中。在這裏作者將Subscription類對象中封裝的方法設置權限的代碼註釋掉了,也是應了剛纔說的,只允許onEvent開頭的方法是public的。

而後又出現了一個typesBySubscriber,看一下它的定義:

private final Map<Object, List<Class<?>>> typesBySubscriber;

從名字也可以很簡單的看出來,它保存了當前訂閱者對象所有可能接受的訂閱事件。

以當前訂閱者對象爲key來從這個map中找出集合,如果不存在就創建一個,然後把eventType訂閱事件放到裏面。剩下的一些代碼也暫時不去考慮,先只看一下主線。

到這裏註冊的邏輯就已經完成了,看起來還是挺簡單的,只是將訂閱者啊,Subscription向集合里加一加。看完了註冊我們再來看一下發布事件的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;
            }
        }
    }

首先在currentPostingThreadState調用get方法,看一下這個對象的定義:

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

是一個ThreadLocal,那麼很顯然每個線程都將維護它自己的postingThreadState對象。

從這個對象中得到當前的事件隊列然後將發佈的事件放到隊列中。

看一下當前如果沒有處於正在發佈的狀態,下行。

保存一下當前是否是在主線程中進行的事件發佈,並且將狀態置爲正在發佈。

而後不斷的從隊列中取出事件調用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));
            }
        }
    }

在這裏我們只看主線的狀態,當最簡單的情況下應該走else中的方法,也就是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;
    }

以當前發佈的事件爲key,從subscriptionsByEventType中找出Subscription的集合,如果把subscriptionsByEventType忘記了可以回到前面再看一下。

然後循環出這個集合中的每個Subscription類的對象調用postToSubscription方法,我們來看一下:

 private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.threadMode) {
            case PostThread:
                invokeSubscriber(subscription, event);
                break;
            case MainThread:
            	
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case BackgroundThread:

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

判斷一下Subscription類對象中封裝的方法中ThreadMode,如果是PostThread那麼直接調用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);
        }
    }

簡單的通過反射來調用了onEventPostThread方法,並且將event事件傳入進去。值得注意的是這裏的postToSubscription方法是在事件發佈的線程中調用的。

如果switch選擇出來的是MainThread,那麼判斷當前發佈事件的線程是否是主線程,如果是直接執行,不是將subscription和event事件入隊mainThreadPoster。

同理BackgroundThread判斷當前是否是主線程,是的話入隊backgroundPoster,不是的話直接調用。

如果是Async則直接入隊asyncPoster。


如果有興趣可以查看一下mainThreadPoster、backgroundPoster和asyncPoster的內容,裏面的實現也比較簡單,這裏就不再贅述。





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章