EventBus是目前Android开发中流行的事件总线框架,本文假定大家已经熟悉EventBus的基本使用,我们一起来研究EventBus背后的原理。

在开始分析原理前,首先需要告知大家,在本文中:

  • 将调用了EventBus.getDefault().register(this);的类称为注册类
  • 将被@Subscribe注解所标记的方法称为订阅方法

好了,下面我们正式开始

一、基本使用

我们来看一个最基本的使用:

1.1、定义事件类

首先定义一个事件类EventLoveState

public class EventLoveState {
    private String loveState;

    public EventLoveState(String loveState) {
        this.loveState = loveState;
    }

    public String getLoveState() {
        return loveState;
    }

    public void setLoveState(String loveState) {
        this.loveState = loveState;
    }
}

1.2、订阅事件

接着我们在MainActivity中对这个事件进行订阅:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        EventBus.getDefault().register(this);
    }

    @Subscribe
    public void eventLoveState(EventLoveState eventLoveState) {
        System.out.println(eventLoveState.getLoveState());
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }
}

1.3、发送事件

这样,当在程序中任何地方使用EventBus.getDefault().post(new EventLoveState("失恋了"));去发送这个事件时,此事件的订阅者(比如MainActivity中的eventLoveState()方法)都会被调用。

二、EventBus的register

为了弄清楚为何短短的一句EventBus.getDefault().register(this),就能使该类中所有被@Subscribe标记的方法订阅某个事件类型,并在发送相应事件类型后,能自动调用方法呢?我们一起来分析其源码吧!

2.1、EventBus.getDefault()

来看一下EventBus.getDefault()

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

这是一个典型的DCL单例模式,通过阅读此代码,我们知道了EventBus默认在应用中是以单例的形式存在。接着我们来看看EventBus的构造方法:

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

EventBus(EventBusBuilder builder) {
    logger = builder.getLogger();
    subscriptionsByEventType = new HashMap<>();
    typesBySubscriber = new HashMap<>();
    stickyEvents = new ConcurrentHashMap<>();
    mainThreadSupport = builder.getMainThreadSupport();
    mainThreadPoster = mainThreadSupport != null ? 
                                                  mainThreadSupport.createPoster(this) : null;
    backgroundPoster = new BackgroundPoster(this);
    asyncPoster = new AsyncPoster(this);
    indexCount = builder.subscriberInfoIndexes != null ?
                                                  builder.subscriberInfoIndexes.size() : 0;
    subscriberMethodFinder = new SubscriberMethodFinder(
                                                              builder.subscriberInfoIndexes,
                                                                builder.strictMethodVerification, 
                                                              builder.ignoreGeneratedIndex);
    logSubscriberExceptions = builder.logSubscriberExceptions;
    logNoSubscriberMessages = builder.logNoSubscriberMessages;
    sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
    sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
    throwSubscriberException = builder.throwSubscriberException;
    eventInheritance = builder.eventInheritance;
    executorService = builder.executorService;
}

不难看出上面的代码就是将EventBusBuilder中字段的值赋值给EventBus中的字段,起到一个初始化的作用(Builder模式)。至于这些字段的作用和含义,现在不做探讨,就算现在说了也记不住,在后面用到的时候再说吧!现在混个眼熟即可,到此为止,EventBus对象就创建完成了。

// 目前需要眼熟的几个成员:
// 用于存储[事件类型]与[订阅者信息]的对应关系
Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
// 用于存储[注册类]与[事件类型]的对应关系
Map<Object, List<Class<?>>> typesBySubscriber;
// 用于查找订阅方法
SubscriberMethodFinder subscriberMethodFinder;

2.2、register()

下面就开始来看关键的register()方法了:

public void register(Object subscriber) {
    // 获取注册类的Class对象
    Class<?> subscriberClass = subscriber.getClass();
    // register分析1
    List<SubscriberMethod> subscriberMethods = 
        subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            //register分析2
            subscribe(subscriber, subscriberMethod);
        }
    }
}

register分析1处,我们能够通过方法的名称以及返回结果大致猜出,这一行代码要做的事情是:根据注册类的Class对象(subscriberClass),找到该注册类中所有的订阅方法信息(List<SubscriberMethod>)。

2.2.1、SubscriberMethod

到这里,我们遇到了一个陌生的类:SubscriberMethod,它的关键代码如下:

public class SubscriberMethod {
    // 方法Method对象
    final Method method;
    // @Subscribe注解中threadMode的值,表示接受事件的线程模式
    final ThreadMode threadMode;
    // 订阅方法所订阅的事件类型
    final Class<?> eventType;
    // @Subscribe注解中priority的值,表示接收同一线程模式事件的优先级
    final int priority;
    // @Subscribe注解中sticky的值,表示是否接收粘性事件
    final boolean sticky;
    String methodString;

    public SubscriberMethod(Method method, Class<?> eventType, ThreadMode threadMode, int priority, boolean sticky) {
        this.method = method;
        this.threadMode = threadMode;
        this.eventType = eventType;
        this.priority = priority;
        this.sticky = sticky;
    }
}

其实它就是用于保存订阅方法(被@Subscribe标记)的信息,其中包括了方法本身Method对象、方法参数类型的Class对象eventType以及注解中的参数值:

  • threadMode:标志着订阅方法被调用时所在的线程,如果没有在@Subscribe显式指定,默认为ThreadMode.POSTING,即发送事件时在哪个线程,接收事件就在哪个线程。
  • priority:表示相同threadMode下订阅方法接收事件的优先级,如果没有在@Subscribe显式指定,默认为0,优先级越高,能越早接收到事件。
  • sticky:是否接收粘性事件,如果没有在@Subscribe显式指定,默认是false。粘性事件指的是,哪怕预先没有注册此事件类型的观察者,在发送了粘性事件后再注册,仍然能收到注册前发送的事件消息。

到此,SubscriberMethod分析完毕,顺便也提了下@Subscribe中的一些参数的含义,在这里大家也不妨对照着看一下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
    ThreadMode threadMode() default ThreadMode.POSTING;
    boolean sticky() default false;
    int priority() default 0;
}

2.2.2、SubscriberMethodFinder # findSubscriberMethods()

回到register分析1处继续看代码

List<SubscriberMethod> subscriberMethods =
    subscriberMethodFinder.findSubscriberMethods(subscriberClass);

根据注册类找到注册类中所有的SubscriberMethod(即所有订阅方法)。subscriberMethodFinder是在创建EventBus时初始化的:

EventBus(EventBusBuilder builder) {
    //...
    subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
                builder.strictMethodVerification, builder.ignoreGeneratedIndex);
    //...
}

跟进到SubscriberMethodFinder类中的findSubscriberMethods()方法一探究竟吧:

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    // Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>()
    // 首先从缓存中取出注册类所包含的订阅方法集合
    // 显然可看出,METHOD_CACHE是以注册类Class对象为key,订阅方法集合为value的Map
    List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
    if (subscriberMethods != null) {
        // 如果缓存中存在,则直接返回
        return subscriberMethods;
    }
        // 如果我们使用的是默认的EventBusBuilder创建的EventBus对象,则此值为false
    if (ignoreGeneratedIndex) {
        subscriberMethods = findUsingReflection(subscriberClass);
    } else {
      // 默认将调用findUsingInfo()方法查找注册类中的注册方法信息
        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;
    }
}

上面代码流程非常清晰,首先根据subscriberClass(注册类Class对象)从METHOD_CACHE缓存中获取订阅方法,如果能获取到就直接返回,否则调用findUsingInfo()方法查找。最终如果找到了注册方法,先将其缓存到METHOD_CACHE中,再返回。如果没有找到,则抛出异常。METHOD_CACHE的意义在于对于同一个注册类多次注册,只需查找一次订阅方法,后续直接从缓存中获取即可,以空间换时间。

2.2.3、SubscriberMethodFinder # findUsingInfo()

根据上面的分析我们知道,如果METHOD_CACHE中没有缓存,则会调用findUsingInfo()方法查找,接下来我们来看findUsingInfo()方法:

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
    // 调用prepareFindState()方法得到一个FindState对象
    FindState findState = prepareFindState();
    // 调用findState的initForSubscriber()初始化findState中的成员
    findState.initForSubscriber(subscriberClass);
    // 第一次findState.clazz就是注册类本身,后续表示注册类的父类,如果存在则继续循环
    while (findState.clazz != null) {
        // 默认使用情况下,getSubscriberInfo()返回null
        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 {
          // 默认情况下最终会调用此方法使用反射查找并获取订阅方法信息
            findUsingReflectionInSingleClass(findState);
        }
        // 查找该注册类是否存在父类,如果存在,将父类Class对象赋值给findState.clazz
        // 继续查找父类的订阅方法;如果不存在父类,则findState.clazz为null,停止循环
        findState.moveToSuperclass();
    }
    // 将findState对象所有资源置空并放入缓存以待下次取出使用,
    // 最终返回获取到的订阅方法信息集合(List<SubscriberMethod>)
    return getMethodsAndRelease(findState);
}

上面的代码也很简单,整体流程就是先初始化FindState对象,查找注册类以及其父类中所有注册方法信息。FindStateSubscriberMethodFinder中的静态内部类,在查找过程中起到辅助的作用,现在先来看看FindState中几个比较重要的成员:

// 存储找到的订阅方法信息
final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
// 注册类对象
Class<?> subscriberClass;
// 注册类或注册类父类对象(调用moveToSuperclass()时改变,默认为subscriberClass)
Class<?> clazz;
// 是否跳过父类,默认为false
boolean skipSuperClasses;

知道了FindState中几个比较重要的成员后,我们来看EventBus是如何得到FindState对象的:

private FindState prepareFindState() {
    synchronized (FIND_STATE_POOL) {
        // POOL_SIZE = 4
        for (int i = 0; i < POOL_SIZE; i++) {
          // FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE]
            FindState state = FIND_STATE_POOL[i];
            if (state != null) {
                FIND_STATE_POOL[i] = null;
                return state;
            }
        }
    }
    return new FindState();
}

会优先从FIND_STATE_POOL数组中取出并使用FindState对象,如果数组中没有可用的FindState对象,则创建新的并返回,第一次时,FIND_STATE_POOL中是没有可用的FindState对象的,因此会new FindState()对象返回,当findState使用完成后,会将其成员数据清空,并放到FIND_STATE_POOL中供下次使用(new是比较浪费时间&内存的)。接下来程序会调用findState.initForSubscriber(subscriberClass)方法,对findState中的成员进行初始化:

void initForSubscriber(Class<?> subscriberClass) {
    // 初始化subscriberClass和clazz都为传入的注册类对象
    this.subscriberClass = clazz = subscriberClass;
    // 不跳过父类
    skipSuperClasses = false;
    subscriberInfo = null;
}

接下来程序会进入while循环,由于初始化时,findState.clazz就是注册类对象本身,因此循环肯定会至少执行一次,执行完一次后,会调用findState.moveToSuperclass()查找注册类有无父类,如果有,则将父类对象赋值给findState.clazz继续进行新一次循环,否则findState.clazz = null循环结束:

void moveToSuperclass() {
    // 如果忽略父类,则直接将clazz赋值为null停止循环
    if (skipSuperClasses) {
        clazz = null;
    } else {
        // 获取父类对象
        clazz = clazz.getSuperclass();
        // 获取父类对象的全限定名
        String clazzName = clazz.getName();
        // 如果以下列包名开头的父类,不需要处理,它们不是用户定义的类,里面不可能存在订阅方法
        if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) {
            clazz = null;
        }
    }
}

2.2.4、SubscriberMethodFinder # findUsingReflectionInSingleClass()

那么重点的方法就是findUsingReflectionInSingleClass(findState)了,它利用反射获取了注册类中所有的订阅方法信息,下面我们就来一起看一下它的具体代码:

private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
        // 获取注册类中所有的方法对象
        // 这个方法在极少数android设备上会崩溃,因此这里用了try...catch
        // 那为什么不直接用getMethods()呢?官方给出的解释是getDeclaredMethods()
        // 效率明显高于getMethods()
        methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {
        // 如果出现异常,则调用getMethods()获取当前类以及其父类中所有public方法对象
        methods = findState.clazz.getMethods();
        // 由于getMethods()已经获取到了父类中的方法对象,因此无需再去查找父类了
        findState.skipSuperClasses = true;
    }
    // for循环遍历所有methods得到method对象
    for (Method method : methods) {
        // 获取方法的修饰符
        int modifiers = method.getModifiers();
        // int MODIFIERS_IGNORE = Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | 
        // SYNTHETIC
        // 如果修饰符是public 且 不是MODIFIERS_IGNORE中的任一个,开始解析
        if ((modifiers & Modifier.PUBLIC) != 0 && 
            (modifiers & MODIFIERS_IGNORE) == 0) {
            // 获取方法的参数列表类型
            Class<?>[] parameterTypes = method.getParameterTypes();
            // 当方法的参数列表只有1个参数时,才符合订阅方法的要求,一个订阅只能订阅一种事件类型
            if (parameterTypes.length == 1) {
                // 取得该方法上的注解Subscribe
                Subscribe subscribeAnnotation =
                    method.getAnnotation(Subscribe.class);
                // 如果该方法确实被@Subscribe所标记
                if (subscribeAnnotation != null) {
                    // 取出第一个参数的类型(事件类型)
                    Class<?> eventType = parameterTypes[0];
                    // 调用findState的checkAdd()方法判断此方法能否添加
                    if (findState.checkAdd(method, eventType)) {
                        // 得到ThreadMode
                        ThreadMode threadMode = subscribeAnnotation.threadMode();
                        // 创建SubscriberMethod对象(包含:注册方法对象,事件类型,线程模式,
                        // 优先级,是否粘性事件),并添加到findState的subscriberMethods集合
                        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所标记,最后根据Method对象以及该方法上的@Subscribe注解获取到SubscriberMethod对象所需的信息并创建它的对象,最后将创建出的SubscriberMethod对象保存到findState.subscriberMethods集合中。唯一需要单独说明一下的就是findState.checkAdd(method, eventType),这里为什么需要做这样的检查呢?我们先来看看checkAdd()方法:

/**
 *
 * @param method 订阅方法的Method对象
 * @param eventType 订阅事件的类型
 * @return true:该订阅方法允许添加至findState.subscriberMethods中   
 *             false:该订阅方法不需添加
 */
boolean checkAdd(Method method, Class<?> eventType) {
    // Map<Class, Object> anyMethodByEventType,它是FindState的成员变量
    // 这里以订阅的事件类型为key,订阅方法的Method对象为value put进此Map
    Object existing = anyMethodByEventType.put(eventType, method);
    if (existing == null) {
        // 若existing为null表示从未订阅过此类型的事件,当然直接返回true
        // 一级检查
        return true;
    } else {
        // 到这里说明anyMethodByEventType中存在了key为当前订阅事件(eventType)的值
        // existing = Map中以前eventType对应的value
        // 二级检查
        if (existing instanceof Method) {
            // 只会在第一次遇到相同事件类型时先调用checkAddWithMethodSignature
            if (!checkAddWithMethodSignature((Method) existing, eventType)) {
                throw new IllegalStateException();
            }
            // 将该事件类型对应的value置位非Method类型
            anyMethodByEventType.put(eventType, this);
        }
        //检查订阅方法是否需要解析为SubscriberMethod对象并添加至集合
        return checkAddWithMethodSignature(method, eventType);
    }
}

此方法分为了两级检查,首先会检查订阅事件类型在anyMethodByEventType是否存在了,如果不存在,则直接允许将该订阅方法添加至findState.subscriberMethods中,如果存在,则调用checkAddWithMethodSignature()进行方法签名的检查。为什么需要两级呢?因为大多数注册类中的订阅方法,一般都不会同时订阅相同类型的事件,此时便无需调用checkAddWithMethodSignature()了。只有当注册类中的多个订阅方法同时订阅了同一事件类型时,才需要进一步验证。一级检查很简单,所以我们重点来看看二级检查:checkAddWithMethodSignature()方法:

private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
    // 清空methodKeyBuilder中的内容
    methodKeyBuilder.setLength(0);
    // 将方法的名字添加到methodKeyBuilder中
    methodKeyBuilder.append(method.getName());
    // 将 > 符号 和 订阅事件类型全限定名点击到methodKeyBuilder中
    methodKeyBuilder.append('>').append(eventType.getName());

    // 假设订阅方法:public void methodName(String name);
    // 此时methodKey是这样的格式:methodName>java.lang.String
    String methodKey = methodKeyBuilder.toString();
    // 获取订阅方法所在类的类名 例如:class MainActivity
    Class<?> methodClass = method.getDeclaringClass();
    // Map<String, Class> subscriberClassByMethodKey,它是FindState的成员
    // key是methodKey(方法名+ > + 参数类型),value是订阅方法所在类的类名
    // methodClassOld是以前在subscriberClassByMethodKey中key为methodKey的value,
    // 如果没有则为null
    Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
    // methodClassOld == null 这个好理解,表示此方法签名从来没存在过,
    // 那当然是返回true
    // 后面一个条件是什么鬼?它判断methodClassOld是否是methodClass自己或它的父类
    // 详细的解释我会在下面说明
    if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
        return true;
    } else {
        subscriberClassByMethodKey.put(methodKey, methodClassOld);
        return false;
    }
}

需要说明的是subscriberClassByMethodKey是一个以方法签名为key,方法所在类对象为valueMap。方法签名的格式为:方法名 + 参数类型的全限定名

现在我们来看一下上文中提到的methodClassOld.isAssignableFrom(methodClass)到底是干什么的?假设当subscriberClassByMethodKey存在相同的方法签名(即存在方法名相同,参数类型相同)时,判断前一个同名方法所在的类是否是现在被检查的同名方法的父类,如果是,同样允许解析这个注册方法并添加到findState.subscriberMethods集合中。

现在的问题是,方法签名一样的情况在什么时候会出现呢?答案是子类重写父类方法时。通过前面的源码阅读,我们知道订阅方法的查找顺序是:先查找注册类中的订阅方法,再查找注册类父类(如果有)中的订阅方法。那按照这个顺序,methodClassOld永远都只能是methodClass的子类,因此methodClassOld.isAssignableFrom(methodClass)的结果永远都是false。要这个判断还有什么意义呢?存在即必有用,还记得获取注册类中所有Method对象的代码吗?

Method[] methods;
try {
    // 获取注册类中所有的方法对象
    // 这个方法在极少数android设备上会崩溃,因此这里用了try...catch
    // 那为什么不直接用getMethods()呢?官方给出的解释是getDeclaredMethods()
    // 效率明显高于getMethods()
    methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
    // 如果出现异常,则调用getMethods()获取当前类以及其父类中所有public方法对象
    methods = findState.clazz.getMethods();
    // 由于getMethods()已经获取到了父类中的方法对象,因此无需再去查找父类了
    findState.skipSuperClasses = true;
}

这段代码出现在findUsingReflectionInSingleClass()开头,一旦getDeclaredMethods()出现了异常,则会使用getMethods()来获取Method对象。而getMethods()会获取当前类以及其父类中所有public方法对象,且不保证顺序,这就导致了可能出现父类的Method对象比子类的Method对象先解析的情况。所以在这种情况下methodClassOld.isAssignableFrom(methodClass)就有意义了,虽然这种情况的几率非常非常小。

因此,checkAdd()方法会做出以下过滤:

  1. 当注册类(子类)重写了父类的订阅方法且子类同样使用@Subscribe标记后,父类的订阅方法将不会被注册
  2. getDeclaredMethods()发送异常时 且 当注册类(子类)重写了父类的订阅方法且子类同样使用@Subscribe标记后,确保注册类的订阅方法一定生效,而其父类的订阅方法可能生效,也可能不生效,取决于Method的排序。(此情况非常罕见)

到此,我们回到2.2.3 findUsingInfo()方法,最后一行代码:

return getMethodsAndRelease(findState);

private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
    // 从findState取出订阅方法集合
    List<SubscriberMethod> subscriberMethods = 
        new ArrayList<>(findState.subscriberMethods);
    // 置空操作
    findState.recycle();
    synchronized (FIND_STATE_POOL) {
        for (int i = 0; i < POOL_SIZE; i++) {
            if (FIND_STATE_POOL[i] == null) {
                // 把findState放入数组,以便下次直接使用
                FIND_STATE_POOL[i] = findState;
                break;
            }
        }
    }
    return subscriberMethods;
}

好了,绕了这么大一圈,终于把注册类中的订阅方法信息解析完毕了。我们总结一下整个获取流程:

  1. register()方法需要得到List<SubscriberMethod>(所有订阅方法信息)
  2. 于是register()方法委托SubscriberMethodFinder对象中的findSubscriberMethods()方法帮忙查找
  3. findSubscriberMethods()会先看缓存中是否存在,如果缓存中存在,则直接告诉register()我找到啦!如果缓存中不存在,则会调用findUsingInfo()进行获取
  4. findUsingInfo()会先初始化FindState对象用于获取辅助,并调用findUsingReflectionInSingleClass()进行反射获取
  5. findUsingReflectionInSingleClass()查找完成后,将最终结果返回给findSubscriberMethods()findSubscriberMethods()将结果放进缓存,并返回给register()

2.2.5、subscribe()

到此为止,我们继续回到register()方法中:

public void register(Object subscriber) {
    // 获取注册类的Class对象
    Class<?> subscriberClass = subscriber.getClass();
    // register分析1
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            //register分析2
            subscribe(subscriber, subscriberMethod);
        }
    }
}

继续往后看,它会循环subscriberMethods,并将注册类对象和订阅方法对象传给subscribe()方法,下面我们一起来看看register分析2处的subscribe()代码:

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    // 得到事件类型
    Class<?> eventType = subscriberMethod.eventType;
    // 创建Subscription对象,并将 注册类对象 和 订阅方法信息对象(subscriberMethod)保存进去
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    // subscriptionsByEventType:
    // key为事件类型
    // value为该事件类型对应的Subscription(注册类+订阅方法信息)集合
    // 取出eventType对应的List<Subscription>
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    if (subscriptions == null) {
        // 如果eventType对应的List<Subscription>为null,则创建一个新集合
        subscriptions = new CopyOnWriteArrayList<>();
        // 以eventType为key,将新集合put到subscriptionsByEventType中
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {
        // 若eventType对应的subscriptions(List<Subscription>)不为null
        // 判断subscriptions中是否存在了newSubscription
        // subscribe分析1
        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) {
            // 根据priority优先级,将newSubscription插入到subscriptions中合适的位置
            // (subscriptions中始终保持priority降序)
            subscriptions.add(i, newSubscription);
            break;
        }
    }

    // typesBySubscriber:key为注册类对象;value为该注册类中的事件类型集合
    // 根据注册类获取注册类中所有的事件类型
    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
    if (subscribedEvents == null) {
        // 如果获取到的事件类型为null,则创建一个新集合
        subscribedEvents = new ArrayList<>();
        // 以注册类对象为key,新建的事件类型集合为value放进typesBySubscriber中
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    // 将eventType(当前订阅方法的订阅事件类型)加入到刚刚新建的集合中
    subscribedEvents.add(eventType);

    // 这里我们暂时不看,等后面分析粘性事件时,再回过来看.
    if (subscriberMethod.sticky) {
        // ...
    }
}

整个subscribe()方法的代码其实也比较简单,几乎每一行代码的作用在注释中已经写得非常详细了。现在我们主要来看subscribe分析1处的代码:

if (subscriptions.contains(newSubscription)) {
    // 如果存在,抛出异常
    throw new EventBusException("Subscriber " + subscriber.getClass() + 
    " already registered to event " + eventType);
}

在这里,EventBus会检查同一事件类型中是否包含了相同的Subscription,这个Subscription其实就是用来存储订阅类对象SubscriberMethod对象:

final class Subscription {
    final Object subscriber;
    final SubscriberMethod subscriberMethod;
    volatile boolean active;

    Subscription(Object subscriber, SubscriberMethod subscriberMethod) {
        this.subscriber = subscriber;
        this.subscriberMethod = subscriberMethod;
        active = true;
    }

    @Override
    public boolean equals(Object other) {
        if (other instanceof Subscription) {
            Subscription otherSubscription = (Subscription) other;
            return subscriber == otherSubscription.subscriber
                    && subscriberMethod.equals(otherSubscription.subscriberMethod);
        } else {
            return false;
        }
    }

    @Override
    public int hashCode() {
        return subscriber.hashCode() + subscriberMethod.methodString.hashCode();
    }
}

我们注意到这个类重写了equals()方法,因此两个Subscription相同的条件就是:

  1. 它们必须属于同一个注册类
  2. 它们的subscriberMethod进行equals()后的结果为true

SubscriberMethod类也对equals()方法进行了重写:

@Override
public boolean equals(Object other) {
    if (other == this) {
        return true;
    } else if (other instanceof SubscriberMethod) {
        // 得到方法签名
        checkMethodString();
        SubscriberMethod otherSubscriberMethod = (SubscriberMethod)other;
        // 得到传入SubscriberMethod对象的方法签名
        otherSubscriberMethod.checkMethodString();
        // 比较两个签名是否完全一样
        return methodString.equals(otherSubscriberMethod.methodString);
    } else {
        return false;
    }
}

private synchronized void checkMethodString() {
    if (methodString == null) {
        StringBuilder builder = new StringBuilder(64);
        // 拼接方法所在类的全限定名
        builder.append(method.getDeclaringClass().getName());
        // 拼接方法的名字
        builder.append('#').append(method.getName());
        // 拼接方法参数的类型
        builder.append('(').append(eventType.getName());
        methodString = builder.toString();
    }
}

在阅读了SubscriptionSubscriberMethodequals()方法后,我们再来总结一下两个Subscription相同的条件是:

  1. 它们必须属于同一个注册类
  2. 它们的成员subscriberMethod中的Method的签名(方法所在类的全限定名 + 方法名 + 参数类型)完全一致

2.3、总结

经过以上步骤,整个register过程已经完成了。回顾下整个register流程其实并不复杂,无非就是反射获取所有订阅方法信息,然后遍历获取到的订阅方法信息,创建Subscription对象。从subscriptionsByEventType取出key为当前订阅方法订阅的事情类型对应的List<Subscription>,并将创建的Subscription加入集合;以订阅类为key,取出该订阅类下所有的订阅类型集合,并将该订阅方法订阅的事件类型加入集合。最后附上一张精心绘制的流程图:

registerL流程图

三、EventBus的unregister

register过程的核心就是填充两个Map

  1. typesBySubscriber:存储每个注册类中所订阅的事件类型集合
  2. subscriptionsByEventType:存储每个事件类型所对应的订阅方法信息(包含了方法所在的类对象)

unregister的过程其实就是register过程的反向操作。在代码中,我们一般会使用:

EventBus.getDefault().unregister(this);

3.1、unregister()

解除当前类中所有订阅方法对事件的订阅。现在我们来看看unregister()方法的具体实现:

public synchronized void unregister(Object subscriber) {
    // 从typesBySubscriber根据注册类对象获取所有的订阅事件类型
    List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
    if (subscribedTypes != null) {
        // 如果订阅类型集合不为空,则遍历得到每一个订阅事件类型
        for (Class<?> eventType : subscribedTypes) {
            // 根据订阅事件类型去subscriptionsByEventType中移除
            // Subscription.subscriber与取消注册的subscriber相同的Subscription
            unsubscribeByEventType(subscriber, eventType);
        }
        // 从typesBySubscriber移除注册类中对应的所有订阅事件类型
        typesBySubscriber.remove(subscriber);
    } else {
        // 如果要解除注册的类中并不存在任何订阅,则打印此log
        logger.log(Level.WARNING, "Subscriber to unregister was not 
        registered before: " + subscriber.getClass());
    }
}

由于比较简单,所以请看注释。

3.1.1、unsubscribeByEventType()

下面我们来看看unsubscribeByEventType()方法:

private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
    // 根据订阅事件类型获取到所有的Subscription对象
    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;
                // 从List<Subscription>中移除
                subscriptions.remove(i);
                i--;
                size--;
            }
        }
    }
}

这个方法做的事情就是根据订阅事件类型获取到List<Subscription>,这个集合中的Subscription只保证订阅方法所订阅的事件类型是传入的eventType,但不能就这样把它干掉,还需要判断Subscription是否属于需要解除注册的类,subscription.subscriber == subscriber就是这个作用。一旦满足了这些条件,就可以放心的将Subscription从集合中移除了。

3.2、总结

解除注册实在是太简单,因此我们直接用一张流程图总结下:

unregisterL流程图

四、EventBus的post

订阅和解除订阅我们都学习完了,现在我们是时候来看一看发送事件的代码了,在日常使用中,我们一般是通过:

EventBus.getDefault().post()来发送事件,订阅了此事件的订阅方法就会收到这个事件。现在我们就从post()方法开刀,来看一看发送事件背后的操作:

4.1、post()

public void post(Object event) {
    // currentPostingThreadState是一个ThreadLocal对象
    // 调用get()方法得到PostingThreadState对象
    PostingThreadState postingState = currentPostingThreadState.get();
    // 获取PostingThreadState对象中的eventQueue(事件队列)
    // 其实在几乎所有情况下,eventQueue中的事件个数都不会超过1
    // 因为PostingThreadState对象是从ThreadLocal中得到的
    // 单线程不可能同时post两个事件
    // 至于为什么这里要使用队列保存,我也不知道,甚至连官方也没有给出明确的解释
    // 详见https://github.com/greenrobot/EventBus/issues/359
    // 如果你知道原因,也欢迎你告知我(握手)
    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;
        }
    }
}

注释已经很详细,这里简单说一下:首先从ThreadLocal中获得了PostingThreadState对象,如果你对ThreadLocal不熟悉,可以看看我之前写的一篇关于ThreadLocal的文章:《ThreadLocal源码阅读记录》

PostingThreadState类非常简单:

final static class PostingThreadState {
    // 事件队列(笔者认为此队列最多只会有1个事件,应该不需要使用队列)
    final List<Object> eventQueue = new ArrayList<>();
    // 标记是否正在发送事件
    boolean isPosting;
    // 标记是否是主线程
    boolean isMainThread;
    // 订阅者
    Subscription subscription;
    // 发送的事件对象
    Object event;
    // 是否取消
    boolean canceled;
}

然后向PostingThreadState对象的eventQueue队列中加入这个事件,注释中有说到此队列存在的必要性,笔者认为PostingThreadState对象是从ThreadLocal中得到的,因此不会存在同一时刻往同一个PostingThreadState对象中存储多个事件的可能,欢迎大家在留言区探讨。

4.1.1、isMainThread()

接下来调用isMainThread()获取发送事件时所在线程是否是主线程:

private boolean isMainThread() {
    // mainThreadSupport.isMainThread() 实际上调用了
    // AndroidHandlerMainThreadSupport的isMainThread()
    return mainThreadSupport != null ? mainThreadSupport.isMainThread() : true;
}

private final MainThreadSupport mainThreadSupport;

EventBus(EventBusBuilder builder) {
    // ...
    mainThreadSupport = builder.getMainThreadSupport();
    // ...
}

MainThreadSupport getMainThreadSupport() {
    if (mainThreadSupport != null) {
        return mainThreadSupport;
        // isAndroidLogAvailable()做的事情是:Class.forName("android.util.Log") != null
    } else if (AndroidLogger.isAndroidLogAvailable()) {
        // getAndroidMainLooperOrNull()返回的是Looper.getMainLooper();
        Object looperOrNull = getAndroidMainLooperOrNull();
        return looperOrNull == null ? null :
                // AndroidHandlerMainThreadSupport类实现了MainThreadSupport接口
                new MainThreadSupport.
                        AndroidHandlerMainThreadSupport((Looper) looperOrNull);
    } else {
        return null;
    }
}

public interface MainThreadSupport {
    boolean isMainThread();
    Poster createPoster(EventBus eventBus);

    class AndroidHandlerMainThreadSupport implements MainThreadSupport {
        // MainLooper
        private final Looper looper;

        public AndroidHandlerMainThreadSupport(Looper looper) {
            // 保存Looper.getMainLooper()
            this.looper = looper;
        }

        @Override
        public boolean isMainThread() {
            // 比较当前线程的Looper是否是MainLooper
            return looper == Looper.myLooper();
        }

        @Override
        public Poster createPoster(EventBus eventBus) {
            return new HandlerPoster(eventBus, looper, 10);
        }
    }
}

通过以上代码我们知道了,在创建EventBus对象时,EventBus的成员MainThreadSupport mainThreadSupport被赋值为AndroidHandlerMainThreadSupport对象,AndroidHandlerMainThreadSupport保存有MainLooper的引用,最终调用mainThreadSupport.isMainThread()比较的就是当前线程的Looper是否是MainLooper来判断是否是主线程。

4.1.2、postSingleEvent()

扯远了,我们回到发送事件的代码,继续往下看:

try {
    // 循环事件队列,发送事件.然而我认为这个事件队列中最多只有一个事件
    while (!eventQueue.isEmpty()) {
        // 调用此方法从队列中取出第一个事件发送
        postSingleEvent(eventQueue.remove(0), postingState);
    }
} finally {
    // 最终都会重置标记
    postingState.isPosting = false;
    postingState.isMainThread = false;
}

在这里,会循环事件队列中的全部事件(我认为只会有一个事件)并调用postSingleEvent()

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    // 得到事件对象的Class对象
    Class<?> eventClass = event.getClass();
    // 用于标记是否找到了事件的订阅方法
    boolean subscriptionFound = false;
    // 是否发送事件的父类事件,默认为true,可在创建EventBus时在Builder中修改
    if (eventInheritance) {
        // 查找此事件的所有父类事件
        List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
        int countTypes = eventTypes.size();
        for (int h = 0; h < countTypes; h++) {
            // 得到事件列表(包含父类事件)中的事件
            Class<?> clazz = eventTypes.get(h);
            // 调用postSingleEventForEventType()方法发送事件
            // 如果有一个事件发送成功,则subscriptionFound为true
            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);
        }
        // sendNoSubscriberEvent:是否在没有订阅方法接收事件时,发送NoSubscribeEvent事件,默认true
        if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                eventClass != SubscriberExceptionEvent.class) {
            // 满足以上条件,调用post发送EventBus提供的NoSubscriberEvent事件
            post(new NoSubscriberEvent(this, event));
        }
    }
}

代码中已经有详细注释,重点我们关注两个地方:

  1. lookupAllEventTypes查找父类Class对象
  2. postSingleEventForEventType发送事件

默认情况下,EventBus会查找并发送事件的父类事件,如果你不希望发送父类事件以提高性能,可以在创建EventBus对象时进行配置。下面我们来分析lookupAllEventTypes()方法:

private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
    synchronized (eventTypesCache) {
        // 由于遍历父类事件也是一个比较耗时的操作,因此做了缓存
        List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
        if (eventTypes == null) {
            eventTypes = new ArrayList<>();
            // 缓存为空,将当前事件类赋值给clazz以便无论如何都将其加入返回结果集合
            Class<?> clazz = eventClass;
            while (clazz != null) {
                // 找到父类,加入集合(第一次就是事件类本身)
                eventTypes.add(clazz);
                // 找实现接口,如果有,则添加
                addInterfaces(eventTypes, clazz.getInterfaces());
                // 获取父类class 没有就null停止循环
                clazz = clazz.getSuperclass();
            }
            // 将结果放进缓存以便下次需要时直接使用
            eventTypesCache.put(eventClass, eventTypes);
        }
        return eventTypes;
    }
}

static void addInterfaces(List<Class<?>> eventTypes, Class<?>[] interfaces) {
    for (Class<?> interfaceClass : interfaces) {
        if (!eventTypes.contains(interfaceClass)) {
            eventTypes.add(interfaceClass);
            addInterfaces(eventTypes, interfaceClass.getInterfaces());
        }
    }
}

至此,获取完毕,非常简单。

4.1.3、postSingleEventForEventType()

获取完事件对象所有的父类类型以后,就需要将此事件分别发送给合法的订阅者了,我们来看看postSingleEventForEventType()的源码:

/**
 * 如果需处理父类事件,则此方法在for循环中调用,将分别向订阅该事件类型或是其父类的订阅者发送
 * 如果不需处理,则此方法只发送给订阅当前事件类型的订阅者
 *
 * @param event        欲发送的事件对象
 * @param postingState PostingThreadState对象
 * @param eventClass   事件的Class对象,用于获取List<Subscription>
 *                     它可能是欲发送事件本身的类对象,也可能是欲发送事件的父类类对象
 *                     或是接口类型Class对象
 * @return 是否完成发送
 */
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
    // [订阅方法+注册类信息集合] 我们可以统称为 [订阅信息集合]
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
        // 根据事件类型从subscriptionsByEventType中得到订阅者信息
        // subscriptionsByEventType之前已经分析过
        // 如果忘记,请查看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;
}

这个方法比较简单,在此不过多说明。最终在这个方法中调用了postToSubscription()方法,它的作用是将事件对象发送给每一个订阅方法:

4.1.4、postToSubscription()

private void postToSubscription(Subscription subscription, 
                                    Object event, boolean isMainThread) {
    // 获取订阅方法注解信息中的threadMode
    switch (subscription.subscriberMethod.threadMode) {
        // POSTING即发送事件在哪个线程,接收事件就在哪个线程
        case POSTING:
            // 因此,直接反射调用
            invokeSubscriber(subscription, event);
            break;
        // MAIN即发送事件时如果在主线程,则直接反射执行订阅方法
        // 否则交给mainThreadPoster处理
        case MAIN:
            if (isMainThread) {
                // 发送事件时处于主线程
                invokeSubscriber(subscription, event);
            } else {
                // 发送事件时不处于主线程
                mainThreadPoster.enqueue(subscription, event);
            }
            break;
        // MAIN_ORDERED即无论发送事件时处于哪个线程,都交由mainThreadPoster处理
        case MAIN_ORDERED:
            if (mainThreadPoster != null) {
                mainThreadPoster.enqueue(subscription, event);
            } else {
                invokeSubscriber(subscription, event);
            }
            break;
        // BACKGROUND即如果发送事件时处于主线程,则交由backgroundPoster处理
        // 否则发送事件时所处的线程,就是订阅方法接收事件时所处的线程
        case BACKGROUND:
            if (isMainThread) {
                backgroundPoster.enqueue(subscription, event);
            } else {
                invokeSubscriber(subscription, event);
            }
            break;
        // ASYNC即无论发送事件时处于哪个线程,都交由asyncPoster处理
        case ASYNC:
            asyncPoster.enqueue(subscription, event);
            break;
        default:
            throw new IllegalStateException("Unknown thread mode: " + 
                            subscription.subscriberMethod.threadMode);
    }
}

在这个方法中,主要是根据订阅方法的threadMode来选择线程的调度。下面我们分部针对5种threadMode,分别分析。

4.2、深入threadMode

4.2.1、POSTING

我们知道当@Subscribe()没有明确指定threadMode时,模式的threadMode就是ThreadMode.POSTING。我们直接来看postToSubscription()中的这段代码:

case POSTING:
    // 直接反射调用
    invokeSubscriber(subscription, event);
    break;

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

看到这里,我们就可以得出这样一个结论:POSTING不会做任何线程切换,接收事件时的线程 = 发送事件时所在的线程

4.2.2、MAIN

当订阅方法被@Subscribe(threadMode = ThreadMode.MAIN)注解所标注后,它的事件接收线程模式就为ThreadMode.MAIN。我们直接来看postToSubscription()中的这段代码:

// MAIN即发送事件时如果在主线程,则直接反射执行订阅方法
// 否则交给mainThreadPoster处理
case MAIN:
    if (isMainThread) {
        // 发送事件时处于主线程
        invokeSubscriber(subscription, event);
    } else {
        // 发送事件时不处于主线程
        mainThreadPoster.enqueue(subscription, event);
    }
break;

首先它判断了发送事件时的线程时否为主线程,如果是则直接反射调用订阅方法,如果不是,则执行了mainThreadPoster.enqueue(subscription, event);

4.2.2.1、HandlerPoster

mainThreadPoster是什么?在EventBus的构造方法中,有这样一行代码:

mainThreadPoster = mainThreadSupport != null ? 
                        mainThreadSupport.createPoster(this) : null;

mainThreadSupport还有印象吗?它实际上就是AndroidHandlerMainThreadSupport对象,它实现了MainThreadSupport接口,自然也实现了createPoster()方法。如果你忘记了,请回头查看4.1.1节。因此mainThreadSupport.createPoster(this)实际上就是return new HandlerPoster(eventBus, looper, 10);注意此处的第二个参数looper是主线程的looper,也许我们已经猜到它的作用了。

HandlerPoster继承自Handler实现了Poster接口,而Poster接口非常简单:

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

当线程模式为MAIN且发送事件时不处于主线程时,就会执行下面这行代码:

// 发送事件时不处于主线程
mainThreadPoster.enqueue(subscription, event);

通过前面的分析我们已经能准确分析出,这里的调用实际上是调用的HandlerPosterenqueue()方法,下面我们就来具体看一看:

public class HandlerPoster extends Handler implements Poster {

    // 维护着PendingPost(待发送事件与订阅者信息)的队列
    private final PendingPostQueue queue;
    private final int maxMillisInsideHandleMessage;
    private final EventBus eventBus;
    // handleMessage是否正在处理PendingPost
    private boolean handlerActive;

    // HandlerPoster在EventBus创建时便会创建
    protected HandlerPoster(EventBus eventBus, Looper looper, 
                                    int maxMillisInsideHandleMessage) {
        // 调用了public Handler(Looper looper)
        super(looper);
        this.eventBus = eventBus;
        this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
        queue = new PendingPostQueue();
    }

    public void enqueue(Subscription subscription, Object event) {
        // 得到PendingPost对象
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            // 将PendingPost对象加入到PendingPostQueue队列中
            queue.enqueue(pendingPost);
            if (!handlerActive) {
                // 将标记位设为true,表示handleMessage正在处理PendingPost
                handlerActive = true;
                // 发送一个Message至MessageQueue
                // 通过looper(MainLooper)使handleMessage被回调在主线程
                if (!sendMessage(obtainMessage())) {
                    throw new EventBusException("Could not send handler message");
                }
            }
        }
    }

    @Override
    public void handleMessage(Message msg) {
       //...
    }
}

enqueue()方法中,首先通过obtainPendingPost()方法得到了PendingPost对象,下面我们先来看看PendingPost

final class PendingPost {
    // PendingPost对象缓存池
    private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();

    Object event;
    Subscription subscription;
    PendingPost next;

    private PendingPost(Object event, Subscription subscription) {
        this.event = event;
        this.subscription = subscription;
    }

    static PendingPost obtainPendingPost(Subscription subscription, Object event) {
        synchronized (pendingPostPool) {
            int size = pendingPostPool.size();
            if (size > 0) {
                // 若缓存池中有数据,则直接复用缓存池中的对象
                PendingPost pendingPost = pendingPostPool.remove(size - 1);
                pendingPost.event = event;
                pendingPost.subscription = subscription;
                pendingPost.next = null;
                return pendingPost;
            }
        }
        // 否则直接创建一个新的PendingPost对象
        return new PendingPost(event, subscription);
    }

    static void releasePendingPost(PendingPost pendingPost) {
        // 在使用完成后,对PendingPost对象字段置空
        pendingPost.event = null;
        pendingPost.subscription = null;
        pendingPost.next = null;
        synchronized (pendingPostPool) {
            // 防止缓存池无限制增长,因此最大不能超过10000
            if (pendingPostPool.size() < 10000) {
                // 将PendingPost对象放入缓存池
                pendingPostPool.add(pendingPost);
            }
        }
    }
}

简单吧,到这里我们发现EventBus中经常使用对象缓存池,之前的FindState,这里的PendingPost都是如此。

继续回到HandlerPosterenqueue()方法中,得到了PendingPost对象后,会将PendingPost对象加入到PendingPostQueue队列中,下面我们一起来看一看PendingPostQueue的实现:

package org.greenrobot.eventbus;

final class PendingPostQueue {
    // 队列头部引用
    private PendingPost head;
    // 队列尾部引用
    private PendingPost tail;

    synchronized void enqueue(PendingPost pendingPost) {
        if (pendingPost == null) {
            throw new NullPointerException("null cannot be enqueued");
        }
        if (tail != null) {
            // 若队列存在尾部,则将尾部的next引用指向当前pendingPost
            tail.next = pendingPost;
            // 将tail更新为当前pendingPost
            tail = pendingPost;
        } else if (head == null) {
            // 若不存在尾部,也不存在头部,则将当前pendingPost当做头部也当做尾部
            head = tail = pendingPost;
        } else {
            throw new IllegalStateException("Head present, but no tail");
        }
        notifyAll();
    }

    synchronized PendingPost poll() {
        // 队列先进先出,取出头部PendingPost对象
        PendingPost pendingPost = head;
        if (head != null) {
            head = head.next;
            if (head == null) {
                tail = null;
            }
        }
        return pendingPost;
    }

    synchronized PendingPost poll(int maxMillisToWait) throws InterruptedException {
        if (head == null) {
            wait(maxMillisToWait);
        }
        return poll();
    }
}

PendingPostQueue中包含了入队与出队的方法,都很简单。将PendingPost加入到队列中后,执行了sendMessage(obtainMessage()),这个时候,HandlerPoster中的handleMessage()方法就会被回调,又由于looperMainLooper,因此它是被回调在主线程的,这样也就完成了线程切换:

public class HandlerPoster extends Handler implements Poster {

    // 维护着PendingPost(待发送事件与订阅者信息)的队列
    private final PendingPostQueue queue;
    private final int maxMillisInsideHandleMessage;
    private final EventBus eventBus;
    // handleMessage是否正在处理PendingPost
    private boolean handlerActive;

    // HandlerPoster在EventBus创建时便会创建
    protected HandlerPoster(EventBus eventBus, Looper looper, 
                                int maxMillisInsideHandleMessage) {
        // ...
    }

    public void enqueue(Subscription subscription, Object event) {
        // ...
    }

    @Override
    public void handleMessage(Message msg) {
        boolean rescheduled = false;
        try {
            long started = SystemClock.uptimeMillis();
            // 不断从PendingPostQueue中取出PendingPost
            while (true) {
                PendingPost pendingPost = queue.poll();
                if (pendingPost == null) {
                    synchronized (this) {
                        // 再次尝试出列
                        pendingPost = queue.poll();
                        if (pendingPost == null) {
                            handlerActive = false;
                            return;
                        }
                    }
                }
                // 调用eventBus中的invokeSubscriber
                // 作用是回收pendingPost对象并反射调用订阅方法
                eventBus.invokeSubscriber(pendingPost);

                long timeInMethod = SystemClock.uptimeMillis() - started;
                // 当时间超出了maxMillisInsideHandleMessage(默认是10)
                // 会重新发一条消息
                // 至于有什么用,目前不太清楚.
                if (timeInMethod >= maxMillisInsideHandleMessage) {
                    if (!sendMessage(obtainMessage())) {
                        throw new EventBusException("Could not send handler message");
                    }
                    rescheduled = true;
                    return;
                }
            }
        } finally {
            handlerActive = rescheduled;
        }
    }
}

可以看到,在handleMessage()中会不停地从PendingPostQueue中取出PendingPost,交给EventBusinvokeSubscriber执行订阅方法的调用:

void invokeSubscriber(PendingPost pendingPost) {
    // 事件对象
    Object event = pendingPost.event;
    // 订阅者信息对象
    Subscription subscription = pendingPost.subscription;
    // PendingPost成员置空
    PendingPost.releasePendingPost(pendingPost);
    if (subscription.active) {
        // 反射调用订阅方法
        invokeSubscriber(subscription, event);
    }
}

到这里,一切都结束了。这就是EventBus为何能够切换线程的奥秘所在。

4.2.3、MAIN_ORDERED

学习了MAIN模式后,MAIN_ORDERED也就没什么可说的了,它只不过是无论发送事件时处于哪个线程,都会统一交由mainThreadPoster处理:

// MAIN_ORDERED即无论发送事件时处于哪个线程,都交由mainThreadPoster处理
case MAIN_ORDERED:
    if (mainThreadPoster != null) {
        mainThreadPoster.enqueue(subscription, event);
    } else {
        invokeSubscriber(subscription, event);
    }
break;

4.2.4、BACKGROUND与ASYNC

有了上面的基础以后,大家可以自行查看BackgroundPoster.javaAsyncPoster.java,在这里简单说明一下:BackgroundPoster会开启一个新的线程来处理队列中的事件;AsyncPoster会为每个事件单独开启一个新的子线程来处理。

4.3、总结

现在我们来总结一下整个post流程:

  1. 通过ThreadLocal得到PostingThreadState对象,将事件对象存入PostingThreadState的队列中
  2. 遍历PostingThreadState的事件对象队列,依次取出事件对象传给postSingleEvent()方法进行下一步
  3. 查找事件对象继承的类和实现的接口,得到它们的Class对象并保存到集合List<Class<?>> eventTypes
  4. 循环eventTypes取出每个Class对象(订阅当前事件或是其父类或接口的订阅方法也能收到此事件),调用postSingleEventForEventType()进行下一步
  5. 根据传入的Class对象从subscriptionsByEventType获得List<Subscription>即所有订阅者信息集合,循环此集合,调用postToSubscription()进行下一步
  6. 根据订阅者信息中的threadMode的值,分别选择不同的调用策略完成订阅方法的反射调用(主要是线程的切换)

五、EventBus的postSticky

EventBus源码分析最后一部分了,加油呀!我们知道EventBus除了发送普通事件以外,还能发送粘性事件,意思是如果我们发送了一个粘性事件后再去订阅此事件,同样能收到消息。这是怎么做到的呢?我们一起来学习这部分的源码吧:

public void postSticky(Object event) {
    synchronized (stickyEvents) {
        // 以事件Class对象为key,事件对象本身为value保存
        stickyEvents.put(event.getClass(), event);
    }
    post(event);
}

只不过在执行正常post流程之前,先将事件对象保存到了stickyEvents这个Map中。现在我们要回到register()方法,来探究一个我们之前跳过的内容。

if (subscriberMethod.sticky) {
    // eventInheritance还熟悉吧
    // false表示仅将事件对象发送给订阅了自己的订阅者
    // true表示还需要考虑发送给订阅了事件对象父类或接口的订阅者
    // 在postSingleEvent()中也用到了此变量判断
    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)) {
                // 如果eventType(订阅类型的Class对象)是粘性Map中取出的事件Class对象的父类
                // 则也需要接收此事件
                Object stickyEvent = entry.getValue();
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    } else {
        // 只关心事件对象本身类型就好
        Object stickyEvent = stickyEvents.get(eventType);
        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
    }
}

private void checkPostStickyEventToSubscription(Subscription newSubscription, 
                                                            Object stickyEvent) {
    if (stickyEvent != null) {
        // 这又回到了post的最后一步
        postToSubscription(newSubscription, stickyEvent, isMainThread());
    }
}

5.1、总结

在发送粘性事件时,会将事件保存在Map中。在订阅者完成订阅的最后一步,会去查找自己订阅的事件类型在粘性事件Map中是否存在,如果存在则取出事件对象,执行发送过程。

六、结束

终于结束了对EventBus的源码分析。从EventBus的源码中收获了不少知识,感谢所有开源者!我们共同进步!



Android技术      源码解析 EventBus 消息总线

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!