EventBus源码分析

前言

首先我们看一下官方是如何介绍EventBus的。EventBus是适用于AndroidJava的开源库,使用发布者/订阅者模式进行松散耦合。EventBus使中央通信只需几行代码即可解耦类,从而简化了代码,消除了依赖关系并加快了应用程序的开发。

知识点准备

在进行源码分析之前,我们要知道EventBus中的三个知识点:角色分配、线程模型和事件类型。

角色分配

  • Event

事件,它可以是任意类型,EventBus会根据事件的类型进行全局发送。

  • Subscriber

事件订阅者,也可以称之为事件的接收者。

  • Publisher

事件的发布者,它可以在任意线程中发布事件。通常调用EventBus.getDefault().post()方法进行事件发送。

线程模型

事件模型是指事件订阅者所在线程和发布事件者所在线程的关系。

  • POSTING

订阅事件的执行和事件发布处于同一线程。这是默认设置。

  • MAIN

Android上,用户将在Android的主线程(UI线程)中被调用。如果发布线程是主线程,则将直接调用订阅方方法,从而阻塞发布线程。否则,事件将排队等待传递(非阻塞)。使用此模式的订阅服务器必须快速返回,以避免阻塞主线程。如果不在Android上,则行为与POSTING相同。

  • MAIN_ORDERED

Android上,用户将在Android的主线程(UI线程)中被调用。与{@link#MAIN}不同,事件将始终排队等待传递。这确保了post调用是非阻塞的。

  • BACKGROUND

Android上,用户将在后台线程中被调用。如果发布线程不是主线程,则将在发布线程中直接调用订阅方方法。如果发布线程是主线程,则EventBus使用单个后台线程,该线程将按顺序传递其所有事件。使用此模式的订阅服务器应尝试快速返回,以避免阻塞后台线程。如果不在Android上,则始终使用后台线程。

  • ASYNC

订阅服务器将在单独的线程中调用这始终独立于发布线程和主线程。发布事件从不等待使用此模式的订阅服务器方法。

事件类型

  • 普通事件

普通事件是指已有的事件订阅者能够收到事件发送者发送的事件,在事件发送之后注册的事件接收者将无法收到事件。发送普通事件可以调用EventBus.getDefault().post()方法进行发送。

  • 粘性事件

粘性事件是指,不管是在事件发送之前注册的事件接收者还是在事件发送之后注册的事件接收者都能够收到事件。对于普通事件的区别之处在于事件接收处需要定义事件接收类型,它可以通过@Subscribe(threadMode = xxx, sticky = true)的方式进行声明;在事件发送时需要调用EventBus.getDefault().postSticky()方法进行发送。事件类型默认为普通事件。

  • 事件优先级

订阅者优先级以影响事件传递顺序。在同一传递线程ThreadMode中,优先级较高的订阅者将在优先级较低的其他订阅者之前接收事件。默认优先级为0。注意:优先级不影响具有不同ThreadMode的订阅服务器之间的传递顺序!

简单使用

首先定义事件(Event)类型

public class SayHelloEvent {
    private String message;

    public void sayHellow(String message) {
        this.message = message;
    }
}

需要在创建需要订阅者是调用EventBus.getDefault().register(this)方法进行注册,在适当的位置调用EventBus.getDefault().unregister(this)方法进行解注册。同时创建一个接收事件的方法onMessageEvent方法,使用注解@Subscribe进行标示,同时生命事件接收的线程为主线程。

@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(SayHelloEvent event) {
    String message = event.getMessage();
    ...
}

@Overridepublic void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}
 
@Overridepublic void onStop() {
    EventBus.getDefault().unregister(this);
    super.onStop();
}

最后在需要发送的时候发送事件即可

EventBus.getDefault().post(new SayHelloEvent("Hello,EventBus!!!"));

初步使用了相关api后,通常会有以下几个疑问:

  1. 如何做到Publisher和Subscriber松耦合

  1. Publisher如何找到Subscriber的订阅方法并执行

  1. 如何切换线程

如何做到Publisher和Subscriber松耦合

这里涉及到比较重要的一个涉及模式,中介者模式(Mediaor Pattern)

中介者模式简介

定义:中介者模式(Mediaor Pattern)包装了一些列对象相互作用的方式,使得这些对象不必相互明显作用。从而使它们可以松散耦合。

中介者模式是用来解决紧耦合问题,该模式将对象之间“多”对“多”的关系转变成“多”对“一”对“多”的关系,其中“一”就是中介者。中介者对象将系统从网状结构变成了以中介者为中心的星形结构。

EventBus一个优雅的中介者

上图是EventBus官网给出的框架示意图,很明显可以看出EventBus作为Publisher(发布者)和Subscriber(订阅者)之间的中介者,用于传输Event。事实上,EventBus作为事件总线类,角色更应该如下图所示:

图中,Publisher表示事件生产者,EventBus表示事件总线,Subscription表示订阅者。EventBus总线持有0或多个时间订阅者,多个Publisher持有EventBus时间总线。 我们假设没有事件总线,发布者和订阅者之间的关系就可能像下图一样纷乱:

EventBus具体实现源码如下,订阅者、自行定义的事件类和Subscription类通过两个HashMap关联在一起。Subscription中记录了subscriber和subscriberMethod

public class EventBus {
    //一个HashMap,以事件类型eventType为key,以存储了订阅者subscriber和订阅方法subscriberMethod的集合CopyOnWriteArrayList<Subscription>为value
    private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
    //一个HashMap,以订阅者subscriber为key,以订阅者所订阅的事件类型eventType的集合List<Class<?>>为value
    private final Map<Object, List<Class<?>>> typesBySubscriber;
}

Publisher如何找到Subscriber的订阅方法并执行

发布者发布事件到订阅者执行事件设计到两个核心方法post&register,下面我们具体分析具体执行过程。

EventBus#register()

public void register(Object subscriber) {
    // 1、通过反射获取到订阅者的Class对象
    Class<?> subscriberClass = subscriber.getClass();
    // 2、通过subscriberMethodFinder对象获取订阅者所订阅事件的集合
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        // 3、遍历集合进行注册
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    // 4、获取事件类型
    Class<?> eventType = subscriberMethod.eventType;
    // 5、封装Subscription对象
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    // 6、通过事件类型获取该事件的订阅者集合
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    // 7、如果没有订阅者订阅该事件
    if (subscriptions == null) {
        // 创建集合,存入subscriptionsByEventType集合中
        subscriptions = new CopyOnWriteArrayList<>();
        subscriptionsByEventType.put(eventType, subscriptions);
    } else { // 8、如果有订阅者已经订阅了该事件
        // 判断这些订阅者中是否有重复订阅的现象
        if (subscriptions.contains(newSubscription)) {
            throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                    + eventType);
        }
    }
    int size = subscriptions.size();
    // 9、遍历该事件的所有订阅者
    for (int i = 0; i <= size; i++) {
        // 按照优先级高低进行插入,如果优先级最低,插入到集合尾部
        if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
            subscriptions.add(i, newSubscription);
            break;
        }
    }
    
    // 10、获取该事件订阅者订阅的所有事件集合
    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
    if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    // 11、将该事件加入到集合中
    subscribedEvents.add(eventType);
    
    // 12、判断该事件是否是粘性事件
    if (subscriberMethod.sticky) {
        if (eventInheritance) { // 13、判断事件的继承性,默认是不可继承
            // 14、获取所有粘性事件并遍历,判断继承关系
            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();
                    // 15、调用checkPostStickyEventToSubscription方法
                    checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                }
            }
        } else {
            Object stickyEvent = stickyEvents.get(eventType);
            checkPostStickyEventToSubscription(newSubscription, stickyEvent);
        }
    }
}

private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
    if (stickyEvent != null) {
        // 16、如果粘性事件不为空
        postToSubscription(newSubscription, stickyEvent, isMainThread());
    }
}


private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    // 17、根据threadMode的类型去选择是直接反射调用方法,还是将事件插入队列
    switch (subscription.subscriberMethod.threadMode) {
        case POSTING:
            invokeSubscriber(subscription, event);
            break;
        case MAIN:
            if (isMainThread) {
                // 18、通过反射的方式调用
                invokeSubscriber(subscription, event);
            } else {
                // 19、将粘性事件插入到队列中
                // 最后还是会调用EventBus.invokeSubscriber(PendingPost pendingPost)方法。
                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);
    }
}

void invokeSubscriber(PendingPost pendingPost) {
    Object event = pendingPost.event;
    Subscription subscription = pendingPost.subscription;
    PendingPost.releasePendingPost(pendingPost);
    if (subscription.active) {
        invokeSubscriber(subscription, event);
    }
}

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


public class SubscriberMethod {
    final Method method; // 处理事件的Method对象
    final ThreadMode threadMode; //线程模型
    final Class<?> eventType; //事件类型
    final int priority; //事件优先级
    final boolean sticky; //是否是粘性事件
    String methodString;
}

final class Subscription {
    final Object subscriber;
    final SubscriberMethod subscriberMethod;
}

小结

在订阅者注册的过程中主要进行了以下几个步骤:

  1. 调用register方法,首先通过反射获取到订阅者的Class对象。

  1. 通过SubscriberMethodFinder对象获取订阅者中所有订阅的事件集合,如果缓存中有,直接返回;如果缓存中没有,反射方式遍历订阅者内部被注解的方法,将这些方法放入到集合中进行返回。

  1. 遍历第2步获取的集合,将订阅者和事件进行绑定。

  1. 在绑定之后会判断绑定的事件是否是粘性事件,如果是粘性事件,直接调用postToSubscription方法,将之前发送的粘性事件发送给订阅者。

EventBus#post()


public void post(Object event) {
    // 1、获取当前线程的PostingThreadState,这是一个ThreadLocal对象
    PostingThreadState postingState = currentPostingThreadState.get();
    // 2、当前线程的事件集合
    List<Object> eventQueue = postingState.eventQueue;
    // 3、将要发送的事件加入到集合中
    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 {
            // 4、只要事件集合中还有事件,就一直发送
            while (!eventQueue.isEmpty()) {
                postSingleEvent(eventQueue.remove(0), postingState);
            }
        } finally {
            postingState.isPosting = false;
            postingState.isMainThread = false;
        }
    }
}

// currentPostingThreadState是包含了PostingThreadState的ThreadLocal对象
// ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据, 并且线程之间的数据是相互独立的。
// 其内部通过创建一个它包裹的泛型对象的数组,不同的线程对应不同的数组索引,每个线程通过get方法获取对应的线程数据。
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
    @Override
    protected PostingThreadState initialValue() {
        return new PostingThreadState();
    }
};

// 每个线程中存储的数据
final static class PostingThreadState {
    final List<Object> eventQueue = new ArrayList<>(); // 线程的事件队列
    boolean isPosting; //是否正在发送中
    boolean isMainThread; //是否在主线程中发送
    Subscription subscription; //事件订阅者和订阅事件的封装
    Object event; //事件对象
    boolean canceled; //是否被取消发送
}

...

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    // 5、获取事件的Class对象
    Class<?> eventClass = event.getClass();
    boolean subscriptionFound = false;
    if (eventInheritance) { // eventInheritance一般为true
        // 6、 找到当前的event的所有 父类和实现的接口 的class集合
        List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
        int countTypes = eventTypes.size();
        for (int h = 0; h < countTypes; h++) {
            Class<?> clazz = eventTypes.get(h);
            // 7、遍历集合发送单个事件
            subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
        }
    } else {
        subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
    }
    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));
        }
    }
}

private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
    synchronized (eventTypesCache) {
        // 获取事件集合
        List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
        if (eventTypes == null) { //如果为空
            eventTypes = new ArrayList<>();
            Class<?> clazz = eventClass;
            while (clazz != null) {
                eventTypes.add(clazz); //添加事件
                addInterfaces(eventTypes, clazz.getInterfaces()); //添加当前事件的接口class
                clazz = clazz.getSuperclass();// 获取当前事件的父类
            }
            eventTypesCache.put(eventClass, eventTypes);
        }
        return eventTypes;
    }
}

//循环添加当前事件的接口class
static void addInterfaces(List<Class<?>> eventTypes, Class<?>[] interfaces) {
    for (Class<?> interfaceClass : interfaces) {
        if (!eventTypes.contains(interfaceClass)) {
            eventTypes.add(interfaceClass);
            addInterfaces(eventTypes, interfaceClass.getInterfaces());
        }
    }
}

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
        // 8、根据事件获取所有订阅它的订阅者
        subscriptions = subscriptionsByEventType.get(eventClass);
    }
    if (subscriptions != null && !subscriptions.isEmpty()) {
        // 9、遍历集合
        for (Subscription subscription : subscriptions) {
            postingState.event = event;
            postingState.subscription = subscription;
            boolean aborted = false;
            try {
                // 10、将事件发送给订阅者
                postToSubscription(subscription, event, postingState.isMainThread);
                aborted = postingState.canceled;
            } finally {
                // 11、重置postingState
                postingState.event = null;
                postingState.subscription = null;
                postingState.canceled = false;
            }
            if (aborted) {
                break;
            }
        }
        return true;
    }
    return false;
}

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    // 12、根据订阅方法的线程模式调用订阅方法
    switch (subscription.subscriberMethod.threadMode) {
        case POSTING: //默认类型,表示发送事件操作直接调用订阅者的响应方法,不需要进行线程间的切换
            invokeSubscriber(subscription, event);
            break;
        case MAIN: //主线程,表示订阅者的响应方法在主线程进行接收事件
            if (isMainThread) { //如果发送者在主线程
                invokeSubscriber(subscription, event);//直接调用订阅者的响应方法
            } else { //如果事件的发送者不是主线程
                //添加到mainThreadPoster的队列中去,在主线程中调用响应方法
                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的队列中,在线程池中调用响应方法
                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);
    }
}

小结

我们来小结一下事件发送都是做了什么。

  1. 获取当前线程的事件集合,将要发送的事件加入到集合中。(通过threadload做线程数据隔离)

  1. 通过循环,只要事件集合中还有事件,就一直发送。

  1. 获取事件的Class对象,找到当前的event的所有父类和实现的接口的class集合。遍历这个集合,调用发送单个事件的方法进行发送。

  1. 根据事件获取所有订阅它的订阅者集合,遍历集合,将事件发送给订阅者。

  1. 发送给订阅者时,根据订阅方法的线程模式调用订阅方法,如果需要线程切换,则切换线程进行调用;否则,直接调用。

总结

至此,EventBus架构剖析内容就到这里了,希望通过文档有所收获。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值