EventBus源码赏析二 —— 主流程

1,200 阅读8分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第21天,点击查看活动详情

创建

一般情况下,我们使用EventBus.getDefault()获取EventBus实例,它采用了 标准的双重锁检查的单例模式。

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也给我们提供了通过构造者获取实例的方法,首先通过EventBus.builder()获取EventBusBuilder类的实例,然后自定义参数获取EventBus实例。

EventBus eventbus = EventBus.builder()
        .logNoSubscriberMessages(true)
        .sendNoSubscriberEvent(true)
        .build()

在通过单例模式获取实例中,内部最后也是通过构造者模式获取的实例,他使用了一个默认的builder。

private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();    
public EventBus() {
    this(DEFAULT_BUILDER);
}

EventBusBuilder主要提供了以下参数供我们使用

public class EventBusBuilder {
    private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
    boolean logSubscriberExceptions = true;
    boolean logNoSubscriberMessages = true;
    boolean sendSubscriberExceptionEvent = true;
    boolean sendNoSubscriberEvent = true;
    boolean throwSubscriberException;
    boolean eventInheritance = true;
    boolean ignoreGeneratedIndex;
    boolean strictMethodVerification;
    ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;
    List<Class<?>> skipMethodVerificationForClasses;
    List<SubscriberInfoIndex> subscriberInfoIndexes;
    Logger logger;
    MainThreadSupport mainThreadSupport;
    EventBusBuilder() {
    }
}
  • logSubscriberExceptions 当订阅者的事件处理方法内部发生异常时,是否打印日志。
  • logNoSubscriberMessages 当没有注册处理当前事件的订阅者时,是否打印日志。
  • sendSubscriberExceptionEventlogSubscriberExceptions相似,当订阅者的事件处理方法内部发生异常时,是否发送SubscriberExceptionEvent消息。
  • sendNoSubscriberEventlogNoSubscriberMessages相似,当没有注册处理当前事件的订阅者时,是否发送NoSubscriberEvent消息。
  • throwSubscriberExceptionlogSubscriberExceptions相似,当订阅者的事件处理方法内部发生异常时,是否抛出EventBusException异常。
  • eventInheritance 事件是否支持继承,如果支持,那么在发送子事件的时候,订阅了父事件的订阅者也能收到此消息。
  • ignoreGeneratedIndexEventbus 3.x版本中,查找订阅者的订阅方法有两种方式,一是运行时反射获取,另一个是通过编译时注解获取,默认为后者,可以将此参数改为true强制使用反射。
  • strictMethodVerification 是否使用严格检查,开启之后如果遇到非法的订阅方法会抛出EventBusException异常,主要包括@Subscribe修饰的方法只能有一个参数,且此方法必须是public,非static,非abstract的。

注册

调用register()方法注册订阅者,源码如下:

public void register(Object subscriber) {
    Class<?> subscriberClass = subscriber.getClass();
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}

首先获取订阅对象的Class,然后通过SubscriberMethodFinder类查找所有的订阅方法并封装在SubscriberMethod中,SubscriberMethod包含订阅方法的关键信息,比如线程模型,优先级,参数,是否为黏性事件等,findSubscriberMethods()方法返回的是一个List,说明一个订阅者可以订阅多种事件。

订阅

查找完成之后,通过subscribe()方法完成订阅。

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    Class<?> eventType = subscriberMethod.eventType;
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    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);
    }
    subscribedEvents.add(eventType);
    //......粘性事件处理
}
  • 这里用到了两个Map,subscriptionsByEventTypeMap<Class<?>, CopyOnWriteArrayList<Subscription>>类型,keyeventType,value是订阅信息集合,typesBySubscriberMap<Object, List<Class<?>>>类型,key为订阅者类,valueeventType集合。
  • 首先,通过SubscriberMethod获取订阅事件的类型eventType,也就是@Subscribe注解修饰方法的参数类型,然后将订阅者subscriber和订阅方法subscriberMethod封装在Subscription实例中,然后通过订阅事件类型eventType从subscriptionsByEventType中获取订阅信息集合,如果该集合为空则创建并加入subscriptionsByEventType,否则判断此集合是否包含最新的newSubscription,如果包含则抛出异常(说明一个订阅者不能重复订阅相同的消息类型),之后按照订阅方法的优先级priority吧最新的订阅信息加入集合。
  • 然后通过订阅者subscriber将它所有的订阅事件类型都保存在typesBySubscriber

粘性事件处理

完成以上动作之后,如果订阅事件是粘性事件,则还需要额外处理

if (subscriberMethod.sticky) {
    if (eventInheritance) {
        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);
    }
}
  • stickyEventsMap<Class<?>, Object>类型,key是粘性事件类型,value是我们通过postSticky()发送的粘性事件
  • 如果eventInheritancetrue,也就是事件支持继承,那么先遍历stickyEvents,找到当前事件类型的子类型,然后调用checkPostStickyEventToSubscription()发送订阅信息,如果不支持继承,则直接发送。

取消注册

注册流程中主要做了三件事

  1. 建立订阅事件类型eventType和订阅信息(包括订阅者subscriber和订阅方法subscriberMethod)的映射,保存在subscriptionsByEventType
  2. 建立订阅者subscriber和他订阅事件类型eventType的映射,保存在typesBySubscriber
  3. 粘性事件处理

除了第三件事,注册流程都是在建立映射关系,取消注册主要就是移除这类关系

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

首先通过typesBySubscriber获取订阅者subscriber的订阅方法subscribedEvents,如果不为null,遍历调用unsubscribeByEventType()方法,然后将订阅者subscribertypesBySubscriberMap中移除。

private void unsubscribeByEventType(Object subscriber, Class<?> 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--;
            }
        }
    }
}

通过订阅事件类型eventTypesubscriptionsByEventType中找到订阅信息,然后遍历订阅信息,找到需要移除的(subscription.subscriber == subscriber)信息,先把其active标志位false(后续不再处理),随后移除。因为在遍历中移除元素,所以subscriptions定义为CopyOnWriteArrayList

发送事件

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

PostingThreadStateEventBus为每个线程保存的线程变量,保证多个线程之前数据互不干扰,它有以下属性

  • eventQueue 保存事件的列表
  • isPosting 是否正在发送事件
  • isMainThread 是否在主线程
  • canceled 是否取消了发送

首先从ThreadLocal中获取PostingThreadState,然后获取其eventQueue并加入当前事件,随后用isPosting确保当前线程没有正在发送事件,然后对isMainThreadisPosting赋值,判断canceled确保能正常发送,最后循环eventQueue,取出事件交给postSingleEvent()处理,这里使用了try-finally,保证了无论是发布事件出现异常还是事件发布完毕,都会对isPostingisMainThread重新赋值,以便下次post能正常进行。

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);
    }
	//.....结果处理
}

postSingleEvent()方法主流程比较清晰,首先获取事件类型,然后判断eventInheritance,如果事件支持继承,则通过lookupAllEventTypes()查找当前事件的所有的类对象,包括超类和接口, 然后遍历交给postSingleEventForEventType()处理,如果不支持继承,则直接由postSingleEventForEventType()处理当前事件。 postSingleEventForEventType()方法返回处理结果,如果失败,进行异常处理

if (!subscriptionFound) {
    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));
    }
}

结果处理由logNoSubscriberMessagessendNoSubscriberEvent决定,或打印日志,或发送NoSubscriberEvent事件

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

通过eventClasssubscriptionsByEventType中取出订阅信息subscriptions,遍历这些信息将对应的值赋给postingState的属性,然后交给postToSubscription处理,在处理过程中,如果遇到异常,重置postingState的属性,直接处理下一个,如果遇到取消发送(canceledtrue),直接break,停止处理

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 {
                // temporary: technically not correct as poster not decoupled from subscriber
                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);
    }
}

Eventbus的订阅方法可以定义线程模式,根据不同的模式执行不同的策略,但是不论那种策略,最后都会由invokeSubscriber处理。

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

直接从订阅信息subscription中获取订阅方法,然后利用反射执行方法。如果遇到InvocationTargetException异常,根据logSubscriberExceptionsthrowSubscriberException参数决定是否打印日志,是否发送异常信息。

postSticky

public void postSticky(Object event) {
    synchronized (stickyEvents) {
        stickyEvents.put(event.getClass(), event);
    }
    post(event);
}

粘性事件的发送与普通事件的发送基本相似,首先根据事件类型将当前事件保存在stickyEvents中,然后调用post直接发送事件,由于stickyEventsMap<Class<?>, Object>类型,每次put的时候如果key相同,都会导致前一个value被覆盖,所以同一事件类型EventBus只是保留最新的。

取消事件

cancelEventDelivery

EventBus支持在事件分发过程中取消事件

public void cancelEventDelivery(Object event) {
    PostingThreadState postingState = currentPostingThreadState.get();
    if (!postingState.isPosting) {
        throw new EventBusException("This method may only be called from inside event handling methods on the posting thread");
    } else if (event == null) {
        throw new EventBusException("Event may not be null");
    } else if (postingState.event != event) {
        throw new EventBusException("Only the currently handled event may be aborted");
    } else if (postingState.subscription.subscriberMethod.threadMode != ThreadMode.POSTING) {
        throw new EventBusException(" event handlers may only abort the incoming event");
    }
    postingState.canceled = true;
}

根据源码可以看出事件取消需要满足以下条件:

  1. 正在发送事件,即postingState.isPosting==true,一般情况下可以在订阅方法内部取消事件,这样就能保证isPostingtrue
  2. 不能取消空事件
  3. 取消的事件必须和正在发布的事件一样
  4. 只有线程模型为ThreadMode.POSTING才可以取消

当取消事件后,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;
            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;
}

可以看到当canceled=true后,后续的订阅方法将不再执行,这也意味着后面的订阅者不会收到消息。