Android EventBus源码解析

前言

上一篇文章自己对EventBus的用法进行了简单的叙述,然后自己又去研究了一下EventBus3.0源码也参考了网上的一些大佬的博客进行进一步的理解,写这一篇文章让自己对EventBus有个好的总结回顾,如有不正确的地方欢迎留言。

一、EventBus简介

在EventBus没出现之前,那时候的开发者一般是使用Android四大组件中的广播进行组件间的消息传递,那么我们为什么要使用事件总线机制来替代广播呢?主要是因为:

广播:耗时、容易被捕获(不安全)。
事件总线:更节省资源、更高效,能将信息传递给原生以外的各种对象。

关于事件其实是一个泛泛的统称,指的是一个概念上的东西(不一定非得用什么Event命),通过查阅官方文档,事件的命名格式并没有任何要求,你可以定义一个对象作为事件,也可以发送基本数据类型如int,String等作为一个事件。后续的源码也可以进步证明(方法的命名并没有任何要求,只是加上@Subscribe注解即可!同时事件的命名也没有任何要求)。

EventBus作为一个消息总线主要有三个组成部分:
事件(Event): 可以是任意类型的对象。通过事件的发布者将事件进行传递。
事件订阅者(Subscriber): 接收特定的事件。
事件发布者(Publisher): 用于通知 Subscriber 有事件发生。可以在任意线程任 意位置发送事件。


在这里插入图片描述

上图解释了整个EventBus的大概工作流程:事件的发布者(Publisher)将事件 (Event)通过post()方法发送。EventBus内部进行处理,找到订阅了该事件 (Event)的事件订阅者(Subscriber)。然后该事件的订阅者(Subscriber)通过 onEvent()方法接收事件进行相关处理。

二、EventBus源码解析

1、订阅者:EventBus.getDefault().register(this);

首先,我们从获取EventBus实例的方法getDefault()开始分析:

public static EventBus getDefault() {
    if (defaultInstance == null) {
        synchronized (EventBus.class) {
            if (defaultInstance == null) {
                defaultInstance = new EventBus();
            }
        }
    }
    return defaultInstance;
}

在getDefault()中使用了双重校验并加锁的单例模式来创建EventBus实例。
接着,我们看到EventBus的默认构造方法中做了什么:

private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();

public EventBus() {
    this(DEFAULT_BUILDER);
}

在EventBus的默认构造方法中又调用了它的另一个有参构造方法,将一个类型为EventBusBuilder的DEFAULT_BUILDER对象传递进去了。这里的EventBusBuilder很明显是一个EventBus的建造器,以便于EventBus能够添加自定义的参数和安装一个自定义的默认EventBus实例。
我们再看一下EventBusBuilder的构造方法:

public class EventBusBuilder {

    ...

    EventBusBuilder() {
    }
    
    ...
    
}

EventBusBuilder的构造方法中什么也没有做,那我么继续查看EventBus的这个有参构造方法:

private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
private final Map<Object, List<Class<?>>> typesBySubscriber;
private final Map<Class<?>, Object> stickyEvents;

EventBus(EventBusBuilder builder) {
    ...
    
   //Map<订阅事件, 订阅该事件的订阅者集合> 键为 Event的类类型(也就是定义订阅方法里面的实体类型),值为元素 Subscription(订阅信息)链表
   //Subscription 类:关注类中两个字段,一个是 Object 类型的 subscriber,该字段即为注册的对象(在 Android 中时常为 Activity) 
   //另一个是 SubscriberMethod 类型,细节如下:
   //subscriberMethod:SubscriberMethod 类型(订阅方法)。关注类中有个字段 eventType 是 Class<?> 类型,代表 Event 的类类型。
    subscriptionsByEventType = new HashMap<>();
    
    // Map<订阅者, 订阅事件集合> (举个例子就是Activity中对应着该Activity中所有的对应的订阅方法)
    typesBySubscriber = new HashMap<>();
    
    // Map<订阅事件类类型,订阅事件实例对象>. 专用于粘性事件处理的一个字,用于判断某个对象是否注册过.
    stickyEvents = new ConcurrentHashMap<>();
    
    //mainThreadPoster:主线程事件发送器,通过它的mainThreadPoster.enqueue(subscription, event)
    //方法可以将订阅信息和对应的事件进行入队,
    //然后通过 handler 去发送一个消息,在 handler 的 handleMessage 中去执行方法。 
    mainThreadSupport = builder.getMainThreadSupport();
    mainThreadPoster = mainThreadSupport != null ? 
    mainThreadSupport.createPoster(this) : null;
    //backgroundPoster:后台事件发送器,通过它的enqueue() 将方法加入到后台的一个队列,
    //最后通过线程池去执行,注意,它在 Executor的execute()方法 上添加了 synchronized关键字 
    //并设立了控制标记flag,保证任一时间只且仅能有一个任务会被线程池执行。
    backgroundPoster = new BackgroundPoster(this);
    //asyncPoster:实现逻辑类似于backgroundPoster,不同于backgroundPoster的保证
    //任一时间只且仅能有一个任务会被线程池执行的特性,asyncPoster则是异步运行的,可以同时接收多个任务
    asyncPoster = new AsyncPoster(this);
    
    ...
    
    
    subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
            builder.strictMethodVerification, builder.ignoreGeneratedIndex);
   
    // 从builder取中一些列订阅相关信息进行赋值
    ...
   
    // 从builder中取出了一个默认的线程池对象,它由Executors的newCachedThreadPool()方法创建,它是一个有则用、无则创建、无数量上限的线程池。
    executorService = builder.executorService;
}

下面看具体的register()中执行的代码。

  public void register(Object subscriber) { 
         //订阅者类型 
         Class<?> subscriberClass = subscriber.getClass(); 
         //判断该类是不是匿名类,如果是匿名类要使用反射 
         boolean forceReflection = subscriberClass.isAnonymousClass(); 
         //获取订阅者全部的响应函数信息(即上面的onNewsEvent()之类的方法) 
         List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(sub scriberClass, forceReflection); 
        //循环每一个事件响应函数,执行 subscribe()方法,更新订阅相关信息 
        for (SubscriberMethod subscriberMethod : subscriberMetho ds) { subscribe(subscriber, subscriberMethod); 
        } 
  }

接着我们查看SubscriberMethodFinder的findSubscriberMethods()方法:

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    // METHOD_CACHE:Map<Class<?>, List<SubscriberMethod>> 类型。键为注册类的 Class(例如:Activity),
    //值为该类中所有 EventBus 回调的方法链表(也就是被 @Subscribe 标记的方法们)。
    List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
    if (subscriberMethods != null) {
        return subscriberMethods;
    }

    //ignoreGeneratedIndex 来判断是否使用生成的 APT 代码去优化寻找接收事件的过程,如果开启了的话,
    //那么将会通过 subscriberInfoIndexes 来快速得到接收事件方法的相关信息。
    //所以各位读者如果没有在项目中接入 EventBus 的 APT,
    //那么可以将 ignoreGeneratedIndex 设为 false 提高性能
    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 {
        //放入缓存中
        METHOD_CACHE.put(subscriberClass, subscriberMethods);
        return subscriberMethods;
    }
}

在这里我们假如ignoreGeneratedIndex为true,看下findUsingReflection()方法

private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
    FindState findState = prepareFindState();
    findState.initForSubscriber(subscriberClass);
    while (findState.clazz != null) {
        // 通过纯反射去获取被 @Subscribe 所修饰的方法
        findUsingReflectionInSingleClass(findState);
        // 将当前 class 的父类 class 赋值给 findState.clazz 
        findState.moveToSuperclass();
    }
    // 重置 FindState 便于下一次回收利用
    return getMethodsAndRelease(findState);
} 
初始化 FindState 对象后,会进入一个 while 循环中,不停地去反射获取当前类和其父类(注意,在 Java 中,如果当前类实现了一个接口,即使该接口的方法被 @Subscribe 所修饰,当前类中的方法也是不包含该注解属性的,所以如果在接口中对某个方法使用了 @Subscribe 修饰然后让类去实现这个接口是没有任何作用的)的订阅方法并添入列表中,最终返回这个列表并重置 FindState 对象利于下一次重复使用。```

这里ignoreGeneratedIndex 默认为false,所以会执行findUsingInfo()方法

```java
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
    // 注释1处
    FindState findState = prepareFindState();
    findState.initForSubscriber(subscriberClass);
    while (findState.clazz != null) {
        // 注释2处
        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 {
             // 注释3处
             findUsingReflectionInSingleClass(findState);
        }
        findState.moveToSuperclass();
    }
    // 重置 FindState 便于下一次回收利用
    return getMethodsAndRelease(findState);
}

注释1处: SubscriberMethodFinder中的prepareFindState()方法:

//会先从 FIND_STATE_POOL 即 FindState 池中取出可用的 FindState(这里的POOL_SIZE为4),
//如果没有的话,则通过代码直接新建 一个新的 FindState 对象。
//由于 FindState 在注册流程中使用频繁且创建耗费资源,故创建 FindState 池复用 FindState 对象
private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];
private FindState prepareFindState() {
    synchronized(FIND_STATE_POOL) {
        for (int i = 0; i < POOL_SIZE; i++) {
            FindState state = FIND_STATE_POOL[i];
            if (state != null) {
                FIND_STATE_POOL[i] = null;
                return state;
            }
        }
    }
    return new FindState();
}

分析下FindState这个类:

static class FindState {
    ....
    void initForSubscriber(Class<?> subscriberClass) {
        this.subscriberClass = clazz = subscriberClass;
        skipSuperClasses = false;
        subscriberInfo = null;
    }
    ...
}

它是 SubscriberMethodFinder 的内部类,这个方法主要做一个初始化、回收对象等工作。

我们在回到SubscriberMethodFinder注释2处:getSubscriberInfo():

private SubscriberInfo getSubscriberInfo(FindState findState) {
    if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
        SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
        if (findState.clazz == superclassInfo.getSubscriberClass()) {
            return superclassInfo;
        }
    }
    if (subscriberInfoIndexes != null) {
        for (SubscriberInfoIndex index: subscriberInfoIndexes) {
            SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
            if (info != null) {
                return info;
            }
        }
    }
    return null;
}

这里由于初始化的时候,findState.subscriberInfo和subscriberInfoIndexes为空,所以这里直接返回null
接着我们查看注释3处的findUsingReflectionInSingleClass()方法:

private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
        // 返回当前类自身方法和显式重载的父类方法
        methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {
        methods = findState.clazz.getMethods();
        findState.skipSuperClasses = true;
    }
    for (Method method: methods) {
        int modifiers = method.getModifiers();
        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");
        }
    }
}

这个方法的逻辑是:
通过反射的方式获取订阅者类中的所有声明方法,然后在这些方法里面寻找以@Subscribe作为注解的方法进行处理,先经过一轮检查,看看findState.subscriberMethods是否存在,如果有的话,将方法名,threadMode,优先级,是否为sticky方法封装为SubscriberMethod对象,添加到subscriberMethods列表中。
而实际上是需要过滤一遍的,讲解 checkAdd() 源码前,请思考以下几个问题:
对于同一个 Event,当前类对该对象使用了多个方法进行了多次订阅,那么如果该 Event 被发射的时候,当前类会如何调用这些方法?
对于同一个 Event,父类对该对象进行了一次订阅,子类重写该订阅方法,那么如果该 Event 被发射的时候,父类子类当中会如何处理这些方法?
解决这些方法就需要去看看 checkAdd() 的底层实现了:

boolean checkAdd(Method method, Class<?> eventType) {
    Object existing = anyMethodByEventType.put(eventType, method);
    if (existing == null) {
        return true;
    } else {
        return checkAddWithMethodSignature(method, eventType);
    }
}

可以看到 anyMethodByEventType 使用了 Event 的 Class 作为键,这像是意味着一个类对于同一个 Event 只能订阅一次,事实上是不是这样,还得继续看看 checkAddWithMethodSignature(),其源码简化如下:

private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
    methodKeyBuilder.setLength(0);
    methodKeyBuilder.append(method.getName());
    methodKeyBuilder.append('>').append(eventType.getName());

    String methodKey = methodKeyBuilder.toString();
    Class<?> methodClass = method.getDeclaringClass();
    Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
    if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
        return true;
    } else {
        subscriberClassByMethodKey.put(methodKey, methodClassOld);
        return false;
    }
}

可以看到 subscriberClassByMethodKey 使用方法名 + '>' + 事件类型作为键,这意味着对于同一个类来说,subscriberClassByMethodKey 肯定不会键重复(毕竟一个类中不能够方法名相同且方法参数、个数都相同),因此它最终会返回 true。这意味着一个类如果使用了多个方法对同一个 Event 对象进行注册,那么当该 Event 对象被发射出来的时候,所有的方法都将会得到回调。
但是当父类执行上述操作的时候,如果子类有「显示」实现父类的订阅方法,那么此时 subscriberClassByMethodKey.put(methodKey, methodClass) 返回值不会为空,且为子类的 Class,此时 if 上分支将会判断子类 Class 是否 isAssignableFrom 父类 Class,这肯定是会为 false 的,这将会走入 if 下分支并返回 false。这意味着当子类「显示」实现父类的订阅方法的时候,如果此时发射指定 Event 的话,父类的订阅方法将不会执行,而仅会执行子类的订阅方法。

返回subscriberMethods之后,register方法的最后会调用subscribe方法:

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    //获取订阅的事件类型 
    Class<?> eventType = subscriberMethod.eventType;
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    //获取订阅该事件的订阅者集合 在subscriptionsByEventType去查找一个CopyOnWriteArrayList ,
    //如果没有则创建一个新的CopyOnWriteArrayList
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
     //把通过register()订阅的订阅者包装成Subscription 对象 Subscription newSubscription 
    //= new Subscription(subscrib er, subscriberMethod); 
    //订阅者集合为空,创建新的集合,并把newSubscription 加入
    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();
    //添加 newSubscription对象,它是一个 Subscription 类,里面包含着 subscriber 和 subscriberMethod 等信息,
    //并且这里有一个优先级的判断,说明它是按照优先级添加的。优先级越高,会插到在当前 List 靠前面的位置
    for (int i = 0; i <= size; i++) {
        if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
        // 根据 priority 大小放入 List 中
            subscriptions.add(i, newSubscription);
            break;
        }
    }
    //对typesBySubscriber 进行添加,这主要是在EventBus的isRegister()方法中去使用的,目的是用来判断这个 Subscriber对象 是否已被注册过
    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
    if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    subscribedEvents.add(eventType);
    //会判断是否是 sticky事件。如果是sticky事件的话,会调用 checkPostStickyEventToSubscription() 方法
    if (subscriberMethod.sticky) {
        //响应订阅事件的父类事件 
        if (eventInheritance) {
            Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
            //循环获得每个stickyEvent事件 
            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);
        }
    }
}

我们来看下checkPostStickyEventToSubscription()方法:

private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
    if (stickyEvent != null) {
        postToSubscription(newSubscription, stickyEvent, isMainThread());
    }
}

可以看到最终是调用了postToSubscription()这个方法来进行粘性事件的发送,对于粘性事件的处理,我们最后再分析,接下来我们看看事件是如何post的。

2、发布者:EventBus.getDefault().post(new Event());

先来看下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;
        }
    }
}

currentPostingThreadState 是一个 ThreadLocal 类,通过它获取到 PostingThreadState 对象,再根据该对象获取到 event 链表(有没有联想到 Android 中的消息机制?),并将传入的 event 塞入该链表。为了控制 Event 出队列不会被调用多次,PostingThreadState 对象有一个 isPosting 来标记当前链表是否已经开始进行回调操作,通过源码可以看到,每次分发完一个 Event 事件,该事件也会被从链表中 remove 出去。可以看下currentPostingThreadState的源码如下:

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

下一步进入postSingleEvent()方法

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    //分发事件的类型
    Class<?> eventClass = event.getClass();
    boolean subscriptionFound = false;
    //默认为true,如果设为 true 的话,它会在发射事件的时候判断是否需要发射父类事件,设为 false,能够提高一些性能。
    if (eventInheritance) {
        //它的作用就是取出 Event 及其父类和接口的 class 列表,当然重复取的话会影响性能,
        //所以它也做了一个 eventTypesCache 的缓存,这样就不用重复调用 getSuperclass() 方法。
        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) {
        ...
    }
}

然后我们在进入postSingleEventForEventType()方法:

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class <?> eventClass) {
    CopyOnWriteArrayList <Subscription> subscriptions;
    synchronized(this) {
     // 获取订阅事件类类型对应的订阅者信息集合.(register函数时构造的集 合) 
        subscriptions = subscriptionsByEventType.get(eventClass);
    }
    if (subscriptions != null && !subscriptions.isEmpty()) {
        for (Subscription subscription: subscriptions) {
            postingState.event = event;
            postingState.subscription = subscription;
            boolean aborted = false;
            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;
}

这里直接根据 Event 类型从 subscriptionsByEventType 中取出对应的 subscriptions对象,最后调用了 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("Unknow thread mode: " + subscription.subscriberMethod.threadMode);
    }
}

其实可以发现只用到了两种方法,一种是 invokeSubscriber 意味着立即调用该方法,另一种是 xxxPoster.enqueue() 意味着需要使用其他线程来执行该方法。
以下是threadMode 哪几种应避免耗时操作,耗时时阻塞的是哪条线程:

  • POSTING:接收事件方法应执行在发射事件方法所在的线程(由于发射事件方法线程可能是主线程,这意味着接收方法不能执行耗时操作,否则会阻塞主线程)
  • MAIN:在 Android 中则接收事件方法应执行在主线程,否则(在 Java 项目中)等同于 POSTING。如果发射事件方法已位于主线程,那么接收事件方法会被「立即」调用(这意味着接收事件方法不能执行耗时操作,否则会阻塞主线程;同时,由于是「立即」调用,所以发射事件方法此时是会被接收事件方法所阻塞的),否则等同于 MAIN_ORDERED
  • MAIN_ORDERED:在 Android 中则接收事件方法会被扔进 MessageQueue 中等待执行(这意味着发射事件方法是不会被阻塞的),否则(在 Java 项目中)等同于 POSTING。
  • BACKGROUND:
    • 在 Android 中
      • 发射事件方法在主线程中执行,则接收事件方法应执行在子线程执行,但该子线程是 EventBus 维护的单一子线程,所以为了避免影响到其他接收事件方法的执行,该方法不应太耗时避免该子线程阻塞。
      • 发射事件方法在子线程中执行,则接收事件方法应执行在发射事件方法所在的线程。
    • 在 Java 项目中,接收事件方法会始终执行在 EventBus 维护的单一子线程中。
  • ASYNC:接收方法应执行在不同于发射事件方法所在的另一个线程。常用于耗时操作,例如网络访问。当然,尽量避免在同一个时间大量触发此类型方法,尽管 EventBus 为此专门创建了线程池来管理回收利用这些线程。

3、解除订阅:EventBus.getDefault().unregister(this);

  public synchronized void unregister(Object subscriber) { 
     // 获取该订阅者所有的订阅事件类类型集合. 
     List<Class<?>> subscribedTypes = typesBySubscriber.get(subsc riber); 
     if (subscribedTypes != null) { 
         for (Class<?> eventType : subscribedTypes) { 
            //subscriptionsByEventType 移除了该 subscriber 的所有订阅信息
              unsubscribeByEventType(subscriber, eventType); 
         }
         // 从typesBySubscriber删除该<订阅者对象,订阅事件类类型集合> 
         typesBySubscriber.remove(subscriber); 
     } else { 
         Log.e("EventBus", "Subscriber to unregister was not regi stered before: "+ subscriber.getClass()); 
     } 
  }
  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 --; 
              } 
         } 
     } 
  }

4、发布黏性瞬间:EventBus.getDefault.postSticky(new CollectEvent())

普通事件是先注册,然后发送事件才能收到;而粘性事件,在发送事件之后再订阅该事件也能收到。此外,粘性事件会保存在内存中,每次进入都会去内存中查找获取最新的粘性事件,除非你手动解除注册

public void postSticky(Object event) {
    synchronized (stickyEvents) {
        // 先将该事件放入 stickyEvents 中
        stickyEvents.put(event.getClass(), event);
    }
    post(event);
}

可以看到第一步是将该事件放入 stickyEvents 中,第二步则是正常 post()。为避免多线程操作 postSticky(Object) 和 removeStickyEvent(Class<?>) 引发的冲突,所以对 stickyEvents 对象添加了 synchronized 关键字,
前面我们在分析register()方法的最后部分时,其中有关粘性事件的源码如下:

if (subscriberMethod.sticky) {
    Object stickyEvent = stickyEvents.get(eventType);
    if (stickyEvent != null) {
        postToSubscription(newSubscription, stickyEvent, isMainThread());
    }
}

可以看到,在这里会判断当前事件是否是 sticky 事件,如果是,则从 stickyEvents 中拿出该事件并执行 postToSubscription() 方法。

好了EventBus源码到此结束了。

总结

EventBus 源码都说简单我可没看出简单啊,当中的很多设计技巧是非常值得学习的,例如前文提到的复用池,以及遍布 EventBus 源码各处的 synchronized 关键字。笔者也是第一次写源码的分析,也是借鉴网上的大佬们的文章。

参考链接:
Android主流三方库源码分析(九、深入理解EventBus源码)
聊一聊 EventBus 源码和设计之禅
EventBus 3.1.1 源码解析

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容

  • 在《Android EventBus使用》博文中,我们已经对EventBus的使用进行了分析,在这篇博文中,我们就...
    24K男阅读 462评论 0 5
  • 基于最新的 3.1.1 分析 前言 之前分析的都是官方库的一些源码,现在打算尝试分析一些比较优秀的第三方开源库,选...
    没有颜色的菜阅读 169评论 0 1
  • EventBus使用 这里推荐一个博客,讲的很流畅~戳这里 源码解析 首先, 我们来看一下获取EventBus对象...
    gustiness阅读 373评论 1 5
  • 对于EventBus 想必作为安卓开发都不会陌生,这边也不会进行讲解使用方法,只是本人在准备面试重新又撸了一遍源码...
    南风向北zhy阅读 333评论 0 0
  • 书当快意读易尽,可有客人期不来。 如今但欲关门睡,一任梅花作雪飞。58起。
    苏州郎心铁阅读 230评论 0 4