之前我们讲过获取EventBus对象的源码,这一篇,我们来讲讲注册的源码。推荐EventBus 3.0进阶:源码及其设计模式 完全解析
简介
1 | /** |
翻译: 注册给订阅方去接收事件,订阅者一旦对接收事件不感兴趣了,就要unregister,订阅者必须要有用Subscribe注解的方法,注解也可以设置线程和优先级
白话文: 订阅者要是想接收消息,必须要先注册。当页面退出,或者不想接收消息的时候必须要反注册,不然他会一直处于接收消息的状态,页面退出会内存泄漏。订阅者的接收方法必须要用Subscribe注解,这个注解的后面可以设置接收这个消息的线程和优先级。如下:
1 | @Subscribe(threadMode = ThreadMode.MAIN,priority = 100,sticky = true) |
就像上面这样写,我一个一个来讲。我们先来说说这个ThreadMode类,点进去,我们可以看到如下内容:
1 | /** |
他就是一个枚举类,几个值的意义,我说的很清楚了。
我们再来讲讲另外两个: sticky,默认值是false,如果设置成true,辣么,这个事件将会是粘性事件。发送事件的方式从post变成了postSticky,其他都没变。
再来讲讲 priority ,默认值是0,在同一个线程中值越大,优先级越高。优先级高的比优先级低的先收到消息。
好,终于准备工作做完了,我们来看看 register() 方法
1 | public void register(Object subscriber) { |
注册方法。首先,他通过反射的方式获得当前类名,然后通过当前类名,找到订阅方法,存到list里面。我们来看看 findSubscriberMethods()方法
1 | List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { |
上面的注释写的很清楚,ignoreGeneratedIndex为false,辣么就会走findUsingInfo() 方法
1 | private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) { |
上面,我们提到了FindState类,我们来看看这个类的代码
1 | static class FindState { |
不难看出,这里的几个map包括了,类名找方法,方法名找类,我们后面都用的到,然后就是初始化方法,前面我们注释里面写了,初始化之后一般信息都是null,这里我们也可以看到。所以,它会走 findUsingReflectionInSingleClass
1 | private void findUsingReflectionInSingleClass(FindState findState) { |
这个方法非常重要!!!在这个方法内部,利用反射的方式,对订阅者类进行扫描判断,是否满足条件从而找出订阅方法,并用上面的容器进行保存。辣么,上面提到的 checkAdd() 方法是怎么检查的呢?
1 | boolean checkAdd(Method method, Class<?> eventType) { |
这个注释写的很清楚,两层检验,第一层是检测事件类型,第二次检验则是检验判断方法的完整,首先以eventType为键,方法为值,存到map中(这个map是在FindState类初始化的),put方法会有一个返回值,返回value,这个value是这个key对应的上一个值,所以说,如果是第一次存放,那么就会返回null。否则,之前存放过,辣么就会进入下一个判断 checkAddWithMethodSignature
1 | private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) { |
这个方法就是用来判断方法签名是否相同的,方法签名是什么呢?就是修饰符+返回类型+方法名+参数list是否相同。如果方法签名相同,辣么,就把旧值赋值给methodClassOld,判断这个值不是为null,第一次调用,没有旧值,就肯定为null,所以,if前面的一个条件是满足的,后面一个条件methodClassOld.isAssignableFrom(methodClass) 的意思是判断旧值是否是methodClass或者同一个类,如果两个条件都不满足,辣么当前方法就不会添加为订阅方法。
那么,说了一大堆关于checkAdd和checkAddWithMethodSignature方法的源码,那么这两个方法到底有什么作用呢?从这两个方法的逻辑来看,第一层判断根据eventType来判断是否有多个方法订阅该事件,而第二层判断根据完整的方法签名(包括方法名字以及参数名字)来判断。下面是笔者的理解:
第一种情况:比如一个类有多个订阅方法,方法名不同,但它们的参数类型都是相同的(虽然一般不这样写,但不排除这样的可能),那么遍历这些方法的时候,会多次调用到checkAdd方法,由于existing不为null,那么会进而调用checkAddWithMethodSignature方法,但是由于每个方法的名字都不同,因此methodClassOld会一直为null,因此都会返回true。也就是说,允许一个类有多个参数相同的订阅方法。
第二种情况:类B继承自类A,而每个类都是有相同订阅方法,换句话说,类B的订阅方法继承并重写自类A,它们都有着一样的方法签名。方法的遍历会从子类开始,即B类,在checkAddWithMethodSignature方法中,methodClassOld为null,那么B类的订阅方法会被添加到列表中。接着,向上找到类A的订阅方法,由于methodClassOld不为null而且显然类B不是类A的父类,methodClassOld.isAssignableFrom(methodClass)也会返回false,那么会返回false。也就是说,子类继承并重写了父类的订阅方法,那么只会把子类的订阅方法添加到订阅者列表,父类的方法会忽略。
让我们回到findUsingReflectionInSingleClass方法,当遍历完当前类的所有方法后,会回到findUsingInfo方法,接着会执行最后一行代码,即return getMethodsAndRelease(findState);那么我们继续 getMethodsAndRelease
1 | private List<SubscriberMethod> getMethodsAndRelease(FindState findState) { |
通过该方法,把subscriberMethods不断逐层返回,直到返回EventBus#register()方法,最后开始遍历每一个订阅方法,并调用subscribe(subscriber, subscriberMethod)方法,那么,我们继续来看subscribe方法。
1 | // Must be called in synchronized block |
到目前为止,注册流程基本分析完毕,丢一张流程图