Android EventBus3源码解析(中)

        前面已经对EventBus订阅者注册注销源码进行了分析,没看过的朋友可以先看:Android EventBus3源码解析(上),本文来解析EventBus3的事件发送原理

post方式发送事件

post发送事件的基本操作如下

EventBus.getDefault().post(new StrMessage("incoming message"));

跳进去看看!

/** Posts the given event to the event bus. */
    public void post(Object event) {
    	//1
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        //2
        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 {
            	//3
                while (!eventQueue.isEmpty()) {
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

        代码不长,首先看代码1处。因为post方法可以在子线程调用也可以在主线程调用,为了避免线程间的干扰,所以postingState是通过ThreadLocal来获取的。postingState字面意思就是当前发送的状态,在不同线程的下状态也可能不同,所以要用ThreadLocal。
        代码2处把传进来的事件加入到队列中,排队等待发送。
        到了代码块3处,就把事件队列里的事件全都发出去。进去postSingleEvent方法看看。

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
		
		//A
        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);
        }
		
		//B
        if (!subscriptionFound) {
            if (logNoSubscriberMessages) {
                logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
            }
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                //C
                post(new NoSubscriberEvent(this, event));
            }
        }
    }

        其实代码可以整体分成两大块,A:有该事件的订阅者,B:没有该事件的订阅者。B比较简单,先分析它。
        我们在使用EventBus时可能发送了一个事件,但这个事件却没有订阅者,这时EventBus就会把这类事件定为 “没人订阅的事件” 并发出去,在代码C处。当然,发出去以后,B代码块就又走了一遍,不过这回C代码不会再运行了。
        回过头来再分析一下有订阅者的情况,也就是A代码块。eventInheritance是指继承事件,如果为false那就只发送本事件,如果为true,那么连同该事件类的父类事件也会被发送。通过lookupAllEventTypes方法就可以获取该事件类型及其父事件类型。
        深入postSingleEventForEventType方法才能知道是怎么发送的。不过在此之前可看到这个方法会返回一个boolean值给subscriptionFound 。其实就是在看这个事件发出去有没有订阅者接收。

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
        	//1
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        if (subscriptions != null && !subscriptions.isEmpty()) {
            //2
            for (Subscription subscription : subscriptions) {
                postingState.event = event;
                postingState.subscription = subscription;
                boolean aborted = false;
                try {
                	//3
                    postToSubscription(subscription, event, postingState.isMainThread);
                    //如果EventBus.getDefault().cancelEventDelivery(event)被调用
                    //那么aborted就为ture
                    aborted = postingState.canceled;
                } finally {
                    postingState.event = null;
                    postingState.subscription = null;
                    postingState.canceled = false;
                }
                //4
                if (aborted) {
                    break;
                }
            }
            return true;
        }
        return false;
    }

        代码1处就是在看有没有这类事件的订阅者,没有的话就直接返回false。
        否则就在2的循环里一个一个地通知。已经很明显了,上文猜测EventBus使用了观察者模式现在得到了证实。
        在不被订阅者截断事件的情况下,每一个该事件的订阅者都会在postToSubscription方法中响应该事件。
        如果有订阅者调用在处理事件时调用了EventBus.getDefault().cancelEventDelivery(event)那么该事件就会被截断,后面的订阅者就接收不到了,也就是在代码4处结束的。
        再深入到postToSubscription方法里面看看是怎么让订阅者接收并处理事件的。

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.threadMode) {
        	//1
            case POSTING:
                invokeSubscriber(subscription, event);
                break;
			//2
            case MAIN:
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
			//3
            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;
                
			//4
            case BACKGROUND:
                if (isMainThread) {
                    backgroundPoster.enqueue(subscription, event);
                } else {
                    invokeSubscriber(subscription, event);
                }
                break;
			//5
            case ASYNC:
                asyncPoster.enqueue(subscription, event);
                break;
            default:
                throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
        }
    }

        整个方法就是一个switch代码块,上面的分支就是我们熟悉的threadMode,在给订阅者写接收事件方法的注解时就会用到这些threadMode,默认下是POSTING方式。不过现在全部都分析一遍。

        首先是POSTING,在代码1处,注释解释道POSTING方法下哪个线程发的,订阅者的接收方法就在哪个线程上执行。再跳进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);
        }
    }

        已经非常明显了,至此,订阅者的接收方法就真正地被调用了。果然是立马就在当前线程下调用的。

        再看看代码块2,是MAIN方式,其实很简单,就是让订阅者接收方法在主线程上执行,(这里也再啰嗦一句,不要在主线程上做耗时的任务)。

        接下来是代码块3,MAIN_ORDERED方式,它和MAIN方式有什么区别呢?其实可以看看注释。

	/**
     * On Android, subscriber will be called in Android's main thread (UI thread). If the posting thread is
     * the main thread, subscriber methods will be called directly, blocking the posting thread. Otherwise the event
     * is queued for delivery (non-blocking). Subscribers using this mode must return quickly to avoid blocking the main thread.
     * If not on Android, behaves the same as {@link #POSTING}.
     */
    MAIN

    /**
     * On Android, subscriber will be called in Android's main thread (UI thread). Different from {@link #MAIN},
     * the event will always be queued for delivery. This ensures that the post call is non-blocking.
     */
    MAIN_ORDERED

        大致意思就是说,Main方式下,如果是在主线程下post的,那就立马执行,如果是在子线程下post的就放在事件队列中排队执行。
        而在MAIN_ORDERED方式下,无论在哪个线程下post,都要排队等待处理。还是有点区别的。

        接下来是代码块5,BACKGROUND方式。代码很简单,就是说如果是在主线程下post的,就把事件处理放到一个后台处理队列排队等待处理。如果是在子线程下post的,那就立马执行。
        上文多次提到把事件放进事件队列,但是光放进去还不够,还得有谁来处理它们。跳进去enqueue方法看看能不能找到这个处理者。

//1
final class BackgroundPoster implements Runnable, Poster {
...
public void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            queue.enqueue(pendingPost);
            if (!executorRunning) {
                executorRunning = true;
                //2
                eventBus.getExecutorService().execute(this);
            }
        }
}
...
//3
@Override
    public void run() {
        try {
            try {
                while (true) {
                	//4
                    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;
                            }
                        }
                    }
                    //5
                    eventBus.invokeSubscriber(pendingPost);
                }
            } catch (InterruptedException e) {
                eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e);
            }
        } finally {
            executorRunning = false;
        }
...

        跳进enqueue方法,再看到代码2的这个地方,然后再看看代码1这个地方。现在应该很清楚了,原来BACKGROUND处理事件是放在子线程中去做的,而这些子线程又是通过线程池来统一管理的(而不是简单粗暴地new Thread().start())。
        具体怎么处理队列的呢?看到代码块3的run方法就很清楚了。每次有一秒钟的等待时间,如果等了这么久还没有事件入列,就返回,这个子线程就结束了,让线程池来处理后事。如果事件队列不为空,订阅者的接收方法就在这里被调用了。由于是while(true)死循环,所以这个子线程可以处理多个事件。
        至此,BACKGROUND方式也就分析完毕了。回过头来再看看最后一种方式。

...
		//5
        case ASYNC:
             asyncPoster.enqueue(subscription, event);
             break;
...

        其实对比一下BACKGROUND方式就会发现ASYNC方式与之很相似,都会避免在主线程下执行,只是说ASYNC方式下,无论是否在子线程下post,事件的的处理都会放到新的子线程去处理。同样,在这个方式下子线程也是通过线程池来管理的。
        到这里post操作就分析完了。

postSticky方式发送事件

使用方法如下

EventBus.getDefault().postSticky(new StrMessage("incoming message"));

跳进去看看。

/**
     * Posts the given event to the event bus and holds on to the event (because it is sticky). The most recent sticky
     * event of an event's type is kept in memory for future access by subscribers using {@link Subscribe#sticky()}.
     */
    public void postSticky(Object event) {
        synchronized (stickyEvents) {
            stickyEvents.put(event.getClass(), event);
        }
        // Should be posted after it is putted, in case the subscriber wants to remove immediately
        post(event);
    }

        代码就比post多了一步,把粘性事件保存在了一个HashMap里。但粘性事件是什么时候被接收的?其实在上文已经解析了一大半了,这里再简单回顾一下。
        当初在Activity注册订阅者的过程中,就已经在监听与之相关的粘性事件了。

...
if (subscriberMethod.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();
                        //1
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
                Object stickyEvent = stickyEvents.get(eventType);
                //2
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
...        

        代码1和代码2就是在查看是不是有与订阅者相关的粘性事件还留着,有的话就拿来处理。看看checkPostStickyEventToSubscription方法。

 private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
        if (stickyEvent != null) {
            // If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
            // --> Strange corner case, which we don't take care of here.
            postToSubscription(newSubscription, stickyEvent, isMainThread());
        }
    }

        果然是postToSubscription方法,这就又回到了post的路上了。如此一来,刚注册的订阅者就能收到粘性事件了。
        本文讲解了事件的收发,剩下的部分留到Android EventBus3源码解析(下)来讲解。

参考文章

eventInheritance的含义:https://blog.csdn.net/wangshihui512/article/details/50947102

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值