EventBus3.0源碼淺讀

EventBus是一個應用於Android和Java的鬆耦合的“主題/訂閱”模式的開源庫。它能夠依賴幾行簡單的代碼解耦總線通信,刪除依賴、提高開發效率。

EventBus Publisher-Subscriber

EventBus的基本組成

EventBus有三個主要部分:
1. Event事件: 任意類型的類對象都可以;
2. Subscriber 事件訂閱者: 事件訂閱者具體處理它鎖訂閱的事件類型的事件的處理。在老版本的EventBus中事件訂閱的處理方法必須是onEvent、onEventMainThread、onEventBackgroundThread、onEventAsync方法來代表不同的事件處理運行的線程模型。在新版本中事件處理方法名稱可以是任何合法方法名,只需要在事件處理方法上添加註解@Subscriber,並且在註解中執行處理方法運行的線程模型。
3. Publisher 事件發佈者:凡是通過EventBus對象的post(xxx)發送了事件的對象都是發佈者。可以在任何線程中發佈,發送的事件會自動分發給想用的事件類型的訂閱者去處理。

EventBus–>ThreadMode介紹:
* POSTING:訂閱者的事件處理函數的線程模型在POSTING模式下,事件是在那個線程下發送的就會在那個線程下執行;需要注意在Android的主線程發送事件時處理的函數的耗時操作的問題,避免出現ANR;
* MAIN:事件處理函數總是運行在主線程,注意ANR問題。
* MAIN_ORDERD:他和MAIN模式一樣,處理函數都是運行在UI trhead中。不同之處在於這種模式下的事件總是排隊交付,不會阻索post()的事件發送。
* BACKGROUND:如果事件發送實在主線程中進程的,處理函數會在新的工作線程中運行;如果發送事件實在工作進程中發出的,處理函數將在於發送事件相同的工作線程中運行。
* ASYNC:總是在新的工作線程中運行。

具體關於EventBus的介紹請去官網詳細瞭解。
EventBus的使用方法較爲簡單在此不再做具體的介紹,直接進入源碼解析。

EventBus類

  EventBus類是EventBus框架的核心類中的核心,它集框架的配置信息、事件訂閱、解除、和發送等功能於一體。在框架使用過程中我們經常使用EventBus.getDefault()的靜態方法獲取一個EventBus實例,也可以通過EventBus.builder()來構建一個EventBus對象。

/** Convenience singleton for apps using a process-wide EventBus instance. */
    //雙重檢查DCL單例模式
    public static EventBus getDefault() {
        EventBus instance = defaultInstance;
        if (instance == null) {
            synchronized (EventBus.class) {
                instance = EventBus.defaultInstance;
                if (instance == null) {
                    instance = EventBus.defaultInstance = new EventBus();
                }
            }
        }
        return instance;
    }

通過單例模式我們可以得到一個對象,這樣不僅簡化了使用方法同所有使用這個對象進行事件的訂閱和發送都是使用這同一個事件總線模型。接下來我們看一下EventBus的構造函數的源碼。

public EventBus() {
        this(DEFAULT_BUILDER);
    }

EventBus(EventBusBuilder builder) {
    logger = builder.getLogger();
    subscriptionsByEventType = new HashMap<>();
    typesBySubscriber = new HashMap<>();
    stickyEvents = new ConcurrentHashMap<>();
    mainThreadSupport = builder.getMainThreadSupport();
    mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
    backgroundPoster = new BackgroundPoster(this);
    asyncPoster = new AsyncPoster(this);
    indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
    subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
            builder.strictMethodVerification, builder.ignoreGeneratedIndex);
    logSubscriberExceptions = builder.logSubscriberExceptions;
    logNoSubscriberMessages = builder.logNoSubscriberMessages;
    sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
    sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
    throwSubscriberException = builder.throwSubscriberException;
    eventInheritance = builder.eventInheritance;
    executorService = builder.executorService;
}

我們從源碼中可以看到EventBus的構造方法是通過一個EventBusBuiler的實例作爲參數來構建對象的。這裏使用了Builer構建者模式進行的複雜對象的靈活構建。其次我們還發現這裏的構造函數是public的,和我們常用的單例模式把構造函數設置爲private不同。主要做的好處時我們可以通過構造函數去創建不同的EventBus的對象,不同的EventBus對象代表着不同的事件總線。這說明我們可以在一個應用程序中使用不同的事件總線。

通過構造方法創建EventBus實例可以根據自己的使用場景配置不同的參數,例子:

 eventBus = EventBus.builder().eventInheritance(params.isEventInheritance()).addIndex(new MyEventBusIndex())
                .ignoreGeneratedIndex(params.isIgnoreGeneratedIndex()).build();

訂閱者註冊

獲取EventBus對象後,就是可以將需要處理事件的訂閱者類註冊到EventBus中,我們常用EventBus.getDefault().register(this)方法把當前類對象註冊到EventBus中。下面看一下register():

    /**
     * 註冊訂閱者接收事件。一但訂閱者不在對接收的事件感興趣必須解除註冊。
     */
    public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        //查找指定訂閱者類的事件處理方法  1
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                //綁定事件處理方法 2
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

在上邊代碼註釋1處是查找指定訂閱者說有的事件處理方法集合,然後將這些事件處理方法(在訂閱者中所有對任何類型事件感興趣的方法)通過subscriber(subscriber, subscriberMethed)進行註冊。register()方法相對簡單,第一找到所有事件處理的方法,然後通過subscribe()將這些方法綁定。

註釋1這裏有一個重要的類開始進入我們的視線,他就是subscriberMethodFinder變量的類。這個成員變量在EventBus對象的構造函數中進行了初始化,那我們看看它是如果通過findSubscriberMethods(SubscriberClass subscriberClass)查找對事件感興趣的方法的:

    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        //從緩存中查找此類的事件方法集合 1
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        }
        /**
        * ignoreGeneratedIndex 表示是否忽略註解生成器生成的MyEventBusIndex, 默認值是false
         *
         * MyEventBusIndex的生成: http://greenbot.org/eventbus/documentation/subscriber-index/
        */
        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 {
            //將找到的方法添加到緩衝中(ConcurrentHashMap)
            METHOD_CACHE.put(subscriberClass, subscriberMethods);
            return subscriberMethods;
        }
    }

findSubscriberMethods方法首先從緩存中查找此訂閱者的事件處理方法。如果緩存中有相關集合將直接返回,退出此方法。在第一次註冊此訂閱者時,緩存中無法找到,返回null。將根據ignoreGenerationIndex的值判讀使用那種方式查找,ignoreGenerationIndex默認值爲false,將調用findusingInfo()。如果訂閱者中沒有任何方法對EventBus的事件(Event)感興趣將會有異常拋出。如果有事件處理方法,將將這些方法加入緩存後返回。

下面我們看看findUsingInfo()方法都幹了哪些事情:

/**
     * 最終找出subscriberClass中的對事件感興趣的方法
     * @param subscriberClass
     * @return
     */
    private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        //準備查找狀態
        FindState findState = prepareFindState();
        //指定訂閱者類
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            //默認實現中subscriberInfo是null  1
            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);
    }

從findusingInfo()方法可以看出首先創建一個FindState類型對象用於記錄查找事件處理方法的結果和狀態。FindState對象的結構很重要我們一會看看FindState的結構,看看它是如何記錄結果的。在EventBus中查找訂閱者中的處理方法是支持查找訂閱者的父類中的事件處理方法的,所以一個while循環不斷循環查找。在註釋1處getSubscriberInfo(FindState findState)的默認實現中是返回值是null,因此方法調用邏輯會轉移到findUsingReflectionSingleClass()方法中,通過反射機制查找訂閱者類中的事件處理方法。在當前訂閱者類中查找完成後會通過findState.moveToSuperclass()方法將查找轉移到父類中進行,直到查找結束。在方法查找結束後會釋放findState對象的狀態。
下面我們具體看一下EventBus是如何通過反射查找事件處理方法的。findUsingRefectionSingleClass()源碼如下:

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();
            //判定是不會public的方法
            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");
            }
        }
    }

查找過程就是通過反射機制查找方法結構,並結合@Subscriber的註解進行方法的查找和一定規則的檢查方法是否符合要求。查找處理的方法信息被封裝在SubscriberMethod對象中,代碼中註釋已經有說明就不再過多介紹。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;
}

在方法查找結束後我們需要將視線重新回到EventBus的regsiter()方法中。在方法查找結束會通過加鎖排他的遍歷事件處理方法列表,並通過調用subscribe(subscriber, subscriberMethod);將方法進行綁定。

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class<?> eventType = subscriberMethod.eventType;
        //將訂閱者類和類中的方法組裝程一個Subscription類對象
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        //查找此事件處理方法所關注的事件類型,然後去查找EventBus中被事件類型的事件的所有訂閱方法
        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);
        }
        //將新註冊的事件類型(Event Type)添加進去
        subscribedEvents.add(eventType);

        /**
         * 判斷一下是不是粘性事件
         */
        if (subscriberMethod.sticky) {
        //粘性事件的處理暫時不做介紹
        ...........
        }
    }

  在做事件綁定前現將訂閱者和事件處理方法組裝成一個Subscription對象,然後根據事件類型去獲取已經訂閱了此事件類型事件的所有處理方法。如果沒有沒有找到就創建一個方法集合放入到EventBus中,並將Subscription對象根據事件處理方法的優先級排序放入其中放入其中。且方法不能重複放入,否則會拋出異常。同時會記錄此訂閱者類都訂閱了哪些事件類型。最後是粘性事件的處理,在此先不談。在subscribe()方法中涉及到EventBus類的幾個重要的結合,我們下面看一下這幾個結合都是幹什麼的;

public class EventBus {
    private static final Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<>();
    //event Type爲key, 以和訂閱了這個event的所有訂閱者集合爲value
    private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
    //以Subscriber爲key, 所訂閱的事件集合爲value
    private final Map<Object, List<Class<?>>> typesBySubscriber;
}

在整個訂閱者註冊的過程中主要涉及的主要類有EventBus、SubscriberMethodFinder、FindState等類。

取消訂閱

在訂閱者不再對訂閱事件感興趣時需要解除綁定,避免出現不必要的事件處理造成邏輯上的錯誤和內存泄漏。比如以Activity對象作爲訂閱者在activity退出時如果不取消訂閱有可能造成內存泄漏。
常用的取消訂閱的方式是EventBus.getDefault().unregister()來需要訂閱者綁定。

    /** Unregisters the given subscriber from all event classes.
     * 取消事件訂閱
     * */
    public synchronized void unregister(Object subscriber) {
        //訂閱的事件類型集合
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
            for (Class<?> eventType : subscribedTypes) {
                unsubscribeByEventType(subscriber, eventType);
            }
            //移除事件
            typesBySubscriber.remove(subscriber);
        } else {
            logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }

方法邏輯較爲簡單,通過訂閱者獲取到此訂閱者所感興趣的事件類型集合。然後遍歷集合中的事件類型並調用unsubscriberByEventType(subscriber, eventType)解除綁定。同時將訂閱者自己從typesBySubscriber集合中刪除。 現在看一下unsubscribeByEventType方法:

private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
        //找到eventType的所有訂閱者
        List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions != null) {
            int size = subscriptions.size();
            for (int i = 0; i < size; i++) {
                Subscription subscription = subscriptions.get(i);
                //將相應的訂閱者的事件處理方法逐一移除
                if (subscription.subscriber == subscriber) {
                    subscription.active = false;
                    subscriptions.remove(i);
                    i--;
                    size--;
                }
            }
        }
    }

通過事件類型查找處所有對此事件類型感興趣的方法,然後在重其中找出屬於subscriber對象的方法。並將這些方法從記錄中刪除。這樣就完成了訂閱者的解綁。

post發送事件

下面我們看一下發送事件的處理邏輯。

    EventBus.geDefault().post(new XxxEvent());

具體看一下EventBus的post()方法;

    public void post(Object event) {
        //將事件添加到當前線程的事件隊列中
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        eventQueue.add(event);

        //啓動事件分發
        if (!postingState.isPosting) {
            postingState.isMainThread = isMainThread();
            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;
            }
        }
    }

事件發送的邏輯比較簡單,首先從ThreadLocal類型對象中獲取一個和post()所在線程綁定的PostingThreadState類型的對象。ThreadLocal是用戶對象和線程綁定的,每個線程只能訪問和自己綁定的對象。因此對綁定對象的訪問是線程安全的。

    /** For ThreadLocal, much faster to set (and get multiple values). */
    final static class PostingThreadState {
        final List<Object> eventQueue = new ArrayList<>();
        boolean isPosting;
        boolean isMainThread;
        Subscription subscription;
        Object event;
        boolean canceled;
    }

可以看出PostingThreadState中有事件隊列、發送狀態、線程標示、訂閱者等信息。我們接着回到post()方法中,在獲取了postingState對象後將事件添加到隊列中。同時通過判斷事件發送狀態,如果未處在發送狀態則啓動一個while (!eventQueue.isEmpty())不斷的從對了中獲取Event事件調用postSingleEvent( Object )將事件發送出去。如果事件發送已經開啓,將事件放入隊列後就會自動發送,在事件發送完成後在finally塊中恢復初始狀態。

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        //eventInheritance 默認是true
        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) {
            //默認爲ture
            if (logNoSubscriberMessages) {
                logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
            }

            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                //發送沒有訂閱者的事件
                post(new NoSubscriberEvent(this, event));
            }
        }
    }

在EventBus的默認配置中eventInheritance 爲true,所以首先調用lookupAllEventTypes()方法查找笨事件類型的父類和接口。然後遍歷事件類型集合調用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 {

                    //TODO 發送到訂閱者方法中
                    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;
    }

本方法邏輯清晰,首先根據事件類型查找訂閱這些事件的處理方法,然後調用postToSubscription(subscription, event, postingState.isMainThread)將事件發送到訂閱者相應的處理方法。

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 MAIN_ORDERED:
                if (mainThreadPoster != null) {
                    mainThreadPoster.enqueue(subscription, event);
                } else {
                    // temporary: technically not correct as poster not decoupled from subscriber
                    invokeSubscriber(subscription, event);
                }
                break;
            case BACKGROUND:
                if (isMainThread) {
                    //Runable的子類
                    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);
        }
    }

postToSubscription方法首先說去事件處理方法的線程模型,然後選擇具體的方法處理所在線程的狀態。在線程選擇完成後都會調用invokeSubscriber(xxx,xxx)方法通過反射調用的方式調用事件處理方法。

      //調用訂閱者方法
    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);
        }
    }
  • PPSTING 線程模式下:程序會調用到invokeSubscriber(subscription, event);此時事件處理方法和事件發送方法在同一個線程中執行

  • MAIN線程模式下:如果發送事件不是在主進程中會調用mainThreadPoster.enqueue(subscription, event);執行事件。mainThreadPoster其實是一個Handler的對象。

  • BACKGROUND線程模式下:如果事件發送是在主進程會調用backgroundPoster.enqueue()方法調用事件處理方法。BackgroundPoster實現了Runnable接口,是一個工作線程。同時將此線程提交給SubscriEventBusBuilder類中一個CachedThreadPool類型的線程池中執行。
  • ASYNC線程模式下:調用asyncPoster.enqueue(subscription, event)執行事件處理方法。AsyncPoster和BackgroundPoster一樣都是實現Runnable接口。

XXXPoster

上邊已經看了各種XXXPoster的調用,下面看一看他們的具體實現。

HandlerPoster

mainThreadPoster的類型是HandlerPoster,繼承自Handler:

public class HandlerPoster extends Handler implements Poster {
    //消息隊列 
    private final PendingPostQueue queue;
    //可執行的最大時間段  方法執行超過這個事件,禁止繼續發送事件
    private final int maxMillisInsideHandleMessage;
    private final EventBus eventBus;

    private boolean handlerActive;

    ....................
}

PendingPostQueue是一個隊列(鏈表),其中存儲組裝了Subscription和Event的PendingPost類型的對象。通過調用HandlerPsoter.enqueue()方法進行消息入隊。

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

在消息入隊後會調用sendMessage(obtainMessage())發送消息。在消息發送後會自動調用handleMessage()方法。

@Override
    public void handleMessage(Message msg) {
        boolean rescheduled = false;
        try {
            //起始時間
            long started = SystemClock.uptimeMillis();
            while (true) {
                //從隊列獲取PendingPost 消息
                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;
        }
    }

handleMessage()不斷從消息隊列中取消息進行方法調用。當方法執行時間超過最大值時,要求新入隊事件不要發送消息過來,直到效力隊列中的事件被執行完畢。

BackgroundPoster 和 AsyncPoster

兩個Poster差別不大,都是實現了Runnable接口。同時都是在同一個線程池中進行執行。內部實現和和HandlerPoster一樣都有一個PendingPostQueue來存儲事件。

final class BackgroundPoster implements Runnable, Poster {

    private final PendingPostQueue queue;
    private final EventBus eventBus;

}

具體的事件處理方法調用在run()方法的內部:

@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) {
                eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e);
            }
        } finally {
            executorRunning = false;
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章