EventBus关于线程的讨论

779 阅读4分钟
  1. EventBus是如何实现事件的发送和线程切换问题?
  2. EventBus能实现进程间通信吗?

当我们调用EventBus.getDefault().post("TAG")发送事件时,EventBus是如何保证事件发送到我们需要的线程中的。一切都要从post说起:

    public void post(Object event) {
        //currentPostingThreadState是一个ThreadLocal对象,
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        //将事件添加到集合中
        eventQueue.add(event);
        //isPosting在有事件正在被处理时 isPosting == true
        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;
            }
        }
    }

post的作用就是获取当前线程对应的PostingThreadState对象,然后与集合中的事件一起发送出去。这个PostingThreadState对象不得了,内部包含的变量与事件发送所在线程密切关联。

    final static class PostingThreadState {
        final List<Object> eventQueue = new ArrayList<>();  //当前线程用于存储事件的集合
        boolean isPosting;    //当前线程有事件在处理时isPosting=true,事件结束isPosting=false
        boolean isMainThread; //前线程是不是主线程
        Subscription subscription; //接收方的信息包装类
        Object event;  // 事件本身
        boolean canceled;  // 事件是否被删除的标志
    }

获取到了PostingThreadState对象后,会调用postSingleEvent方法,该方法的用处在于筛选出发送事件的类型。

    private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        // 默认为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);
        }
       ......
    }

默认情况下eventInheritance=true,表示考虑事件的层次结构,也就是说会按照事件的类型、实现的接口类型、超类那类型以及实现的接口来单独发送事件。当然我们也可以设置eventInheritance=false表示只考虑事件本身类型。

既然上面线程、事件类型都考虑了,下面也应该是事件具体接收了:

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

该方法用于获取事件对应的接收方法的具体信息,存储在subscriptions集合中。这里的subscriptions就对应着我们使用Subscribe注解修饰的方法。最后调用postToSubscription方法,不用说了,肯定得调用修饰的方法了。

    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 {
                    // 一般不会走到这里
                    invokeSubscriber(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);
        }
    }

postToSubscription方法中会采用Subscribe注解设置的ThreadMode来执行线程调度。当然最终的方法的调起还是会执行到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);
        }
    }

EventBus中线程调度的具体接口为Poster,可以看到需要实现的方法enqueue,需要执行线程调度的事件会有一个入队的操作。

interface Poster {
    void enqueue(Subscription subscription, Object event);
}

Poster具体的实现类有三种:

  • HandlerPoster 主线程切换类,内部持有主线程的Looper对象,采用Handler切换线程。
  • BackgroundPoster 采用EventBus中设置的线程池来执行事件,会延迟1s执行。
  • AsyncPoster 采用EventBus中设置的线程池来执行事件。

到这里最开始的第一个问题就有了答案

1. EventBus是如何实现事件的发送和线程切换问题?

EventBus中通过使用Subscribe注解设置的ThreadMode来执行线程调度,分为以下几种情况:

  1. POSTING 事件订阅者和发布者所在线程一致。
  2. MAIN 如果发布者位于主线程则直接回调,否则内部通过Handler来切换到主线程。
  3. MAIN_ORDERED 不区分发布者所在线程,内部通过Handler来切换到主线程。
  4. BACKGROUND 如果发布者在子线程,则直接回调,否则通过EventBus内部线程池切换到子线程执行。
  5. ASYNC 通过EventBus内部线程池切换到子线程执行。

2. EventBus能实现进程间通信吗?

很干脆,不能。