Eventbus是如何實現事件分發的

Evenbus是基於java反射機制來實現事件分發的

  1. EventBus.getDefault().register(this);這裏的this一般情況下是Activity類,但其實它只要是一個Object就可以了,Eventbus支持任何對象的事件分發

Eventbus的regist流程

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

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

當Eventbus註冊之後實際上是在這裏面對這個類的所有方法做了一個遍歷檢測,然後篩選出符合條件的方法,這裏面有一個訂閱的概念,相當於Eventbus幫我們篩選出合適的方法,然後封裝成一個SubscriberMethod對象,這個對象裏面包含了篩選出來的方法的屬性,包括方法名,參數類型,也就是說Eventbus幫我們訂閱監聽了這些方法,這些事情是在subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());裏面做的:

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

這裏面還做了一個緩存優化,如果之前這個對象已經regist過並且還未unRegist,那麼對象方法的訂閱信息會保存在methodCache裏面,key就是類名,這時候要是有同一個類的對象,無論是不是同一個對象,都將直接去methodCache裏面去取相關的方法訂閱信息,如果methodCache沒有這個類的訂閱信息,那麼就繼續往下執行,首先:

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

首先,jdk android sdk系統類全部屏蔽,接着方法名必須是以“onEvent”(ON_EVENT_METHOD_NAME = “onEvent”)開頭的,方法參數必須是隻有一個參數,方法必須是public類型,且不能是static,abstract修飾的方法,然後就接下去分析方法全名:

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

從這裏可知訂閱的方法必須是onEvent,onEventMainThread,onEventBackground,onEventAsync四種纔可被訂閱

Eventbus的4種線程模型

先總結下能被成功訂閱的方法條件:
1. 只有onEvent,onEventMainThread,onEventBackground,onEventAsync這四種方法能被訂閱
2. public方法,並且不能是static,abstract修飾
3. 方法的參數只能是一個
然後我們來看下Eventbus裏面定義的一些線程模型:
1. PostThread:當前線程,對應onEvent()方法,也就是說onEvent()方法裏面執行的代碼和當前調用onEvent()方法的線程屬於同一個線程,可能是主線程,也可能是單獨的線程
2. MainThread:主線程,對應onEventMainThread(),onEventMainThread()方法裏面的代碼一定只會在主線程中執行,所以可以在onEventMainThread()方法中直接進行UI更新操作,但是不能進行耗時操作
3. BackgroundThread:後臺線程,對應onEventBackground(),如果當前線程是主線程,那麼onEventBackground()裏面的代碼會被單獨開一個線程來執行,如果當前的線程已經屬於後臺線程,那麼onEventBackground()裏面的代碼將會直接在這個後臺線程裏面執行,不能執行UI操作,
4. Async:單獨的異步線程,對應onEventAsync(),無論onEventAsync()所在的線程是在主線程或者是在後臺線程,onEventAsync()裏面的代碼都會被單獨開闢一個新的線程來執行,所以在這裏面可以執行一些異步操作,但是不能執行UI操作
比較:BackgroundThread和Async的區別就在於Async裏面可以執行耗時較長操作,因爲每個操作都是一個單獨的線程,但是BackgroundThread就不能執行耗時較長的操作,BackgroundThread的後臺線程是排隊執行的,如果有一個耗時較長的操作在BackgroundThread裏面執行,那麼會導致其他的BackgroundThread事件分發延遲,因爲BackgroundThread正在執行上一個任務
繼續往下分析:
SubscriberMethod是一個訂閱方法的信息類,一個訂閱方法對應一個SubscriberMethod,如果訂閱類裏面有多個訂閱方法,這個訂閱類將會有一個SubscriberMethod的列表信息,裏面保存了訂閱方法,被訂閱的方法的線程模型,以及訂閱方法的參數類型,以下就是SubscriberMethod的信息:

final Method method;
    final ThreadMode threadMode;
    final Class<?> eventType;
    /** Used for efficient comparison */
    String methodString;

    SubscriberMethod(Method method, ThreadMode threadMode, Class<?> eventType) {
        this.method = method;
        this.threadMode = threadMode;
        this.eventType = eventType;
    }

在訂閱方法都遍歷初始化完成後會生成一個SubscriberMethod的List,然後把這個List保存在一個全局的map當中,至此註冊結束

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

事件對象首先是被添加到一個List列表中,其中PostingThreadState是當前線程的分發事件的一些狀態記錄,其中也包含了一個List事件列表,只要是同一個線程的事件分發都會被添加到這裏面來,然後就開始反覆的從List列表中去取出事件並且post出去,執行的是postSingleEvent(eventQueue.remove(0), postingState):

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

我們不看其他的條件判斷,直接看postSingleEventForEventType(event, postingState, eventClass),繼續把事件信息往下傳遞:

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

剛纔註冊的時候不是把訂閱對象的信息都保存到一個map裏面了嗎,就是這個subscriptionsByEventType,這裏從subscriptionsByEventType中get出跟事件對象類型相匹配的訂閱方法信息,繼續把事件對象往下傳遞postToSubscription(subscription, event, postingState.isMainThread):

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

好了,一層一層傳遞下來這裏纔是真正調用剛纔訂閱的那四個方法(onEvent,onEvent…)的代碼,很明顯,採用的是反射機制來調用的,大家最後都調用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);
        }
    }

直接採用反射執行上述四個方法,於是乎,你在regist的時候的那個Object裏面的四個onEvent等方法就能被成功調用執行了

Eventbus保證線程模型執行實現

現在來討論下Eventbus是如果實現後臺線程,主線程運行的,可以看到主線程採用mainThreadPoster.enqueue(subscription, event)執行,後臺線程採用backgroundPoster.enqueue(subscription, event)執行,單獨的異步線程採用asyncPoster.enqueue(subscription, event)執行,一個一個來看;
mainThreadPoster.enqueue(subscription, event):

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

mainThreadPoster是一個Handler,它直接跟主線程的Looper綁定,mainThreadPoster裏面維護了一個隊列PendingPostQueue,所以把事件對象加入隊列後,反覆的從隊列中去出事件對象然後用反射執行方法,代碼如下:

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) {
                        // Check again, this time in synchronized
                        pendingPost = queue.poll();
                        if (pendingPost == null) {
                            handlerActive = false;
                            return;
                        }
                    }
                }
                eventBus.invokeSubscriber(pendingPost);
                long timeInMethod = SystemClock.uptimeMillis() - started;
                if (timeInMethod >= maxMillisInsideHandleMessage) {
                    if (!sendMessage(obtainMessage())) {
                        throw new EventBusException("Could not send handler message");
                    }
                    rescheduled = true;
                    return;
                }
            }
        } finally {
            handlerActive = rescheduled;
        }
    }

所以對於多個事件分發到主線程中執行是一個一個執行的,而不是併發的,可以保證先post的先執行
BackgroundPoster是一個Runnable:

final class BackgroundPoster implements Runnable {

    private final PendingPostQueue queue;
    private final EventBus eventBus;

    private volatile boolean executorRunning;

    BackgroundPoster(EventBus eventBus) {
        this.eventBus = eventBus;
        queue = new PendingPostQueue();
    }

    public void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            queue.enqueue(pendingPost);
            if (!executorRunning) {
                executorRunning = true;
                eventBus.getExecutorService().execute(this);
            }
        }
    }

    @Override
    public void run() {
        try {
            try {
                while (true) {
                    PendingPost pendingPost = queue.poll(1000);
                    if (pendingPost == null) {
                        synchronized (this) {
                            // Check again, this time in synchronized
                            pendingPost = queue.poll();
                            if (pendingPost == null) {
                                executorRunning = false;
                                return;
                            }
                        }
                    }
                    eventBus.invokeSubscriber(pendingPost);
                }
            } catch (InterruptedException e) {
                Log.w("Event", Thread.currentThread().getName() + " was interruppted", e);
            }
        } finally {
            executorRunning = false;
        }
    }

}

話不多,還是用隊列來保證先post的先執行
AsyncPoster類:

class AsyncPoster implements Runnable {

    private final PendingPostQueue queue;
    private final EventBus eventBus;

    AsyncPoster(EventBus eventBus) {
        this.eventBus = eventBus;
        queue = new PendingPostQueue();
    }

    public void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        queue.enqueue(pendingPost);
        eventBus.getExecutorService().execute(this);
    }

    @Override
    public void run() {
        PendingPost pendingPost = queue.poll();
        if(pendingPost == null) {
            throw new IllegalStateException("No pending post available");
        }
        eventBus.invokeSubscriber(pendingPost);
    }

}

AsyncPoster跟BackgroundPoster實現類似,區別就在於BackgroundPoster在事件壓入隊列後在run方法裏面循環poll出事件來執行,而AsyncPoster是把事件壓入隊列後直接單獨開一個線程執行。沒聽明白是不?前面不是提到BackgroundThread和Async線程模型的區別在於BackgroundThread要是執行耗時較長的任務會導致後面的事件延遲執行而Async則不會,因爲AsyncPoster的enqueue()方法在把事件壓入事件隊列後立刻eventBus.getExecutorService().execute(this),而eventBus.getExecutorService()是Executors.newCachedThreadPool()創建的線程池,沒有線程數的限制,所以事件來幾個就執行幾個;而BackgroundPoster的enqueue()在把事件壓入事件隊列後只會執行一次eventBus.getExecutorService().execute(this),然後在run方法中開啓循環來從事件隊列裏面取出事件來執行eventBus.invokeSubscriber(pendingPost),invokeSubscriber()方法就是反射執行onEventBackGround()方法,如果在onEventBackGround()裏面耗時太久,那麼這次循環就一直沒結束,無法進入下一次循環所以會造成BackgroundThread線程模型的事件一直分發下來壓入隊列,但是一直無法執行,只有等上一個耗時任務執行完成才能繼續下一個

Eventbus總結

總結,Eventbus的幾個核心類
1. EventBus,這是個單例,主要是維護註冊對象的訂閱方法信息,對外提供Eventbus的使用接口
2. SubscriberMethod 訂閱方法信息封裝對象
3. AsyncPoster,BackgroundPoster,HandlerPoster保證訂閱方法在各個執行的線程模型裏面執行
4. PendingPostQueue訂閱事件的隊列實現

final class PendingPostQueue {
    private PendingPost head;
    private PendingPost tail;

    synchronized void enqueue(PendingPost pendingPost) {
        if (pendingPost == null) {
            throw new NullPointerException("null cannot be enqueued");
        }
        if (tail != null) {
            tail.next = pendingPost;
            tail = pendingPost;
        } else if (head == null) {
            head = tail = pendingPost;
        } else {
            throw new IllegalStateException("Head present, but no tail");
        }
        notifyAll();
    }

    synchronized PendingPost poll() {
        PendingPost pendingPost = head;
        if (head != null) {
            head = head.next;
            if (head == null) {
                tail = null;
            }
        }
        return pendingPost;
    }

    synchronized PendingPost poll(int maxMillisToWait) throws InterruptedException {
        if (head == null) {
            wait(maxMillisToWait);
        }
        return poll();
    }

}

其實PendingPostQueue只有一個頭部和尾部,說是隊列,其實應該端鏈表,PendingPost不斷的next連着下一個PendingPost

發佈了52 篇原創文章 · 獲贊 35 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章