前面学习了EventBus3.0的基本用法和发送黏性事件等,然而目前为止我们都是只知其然不知其所以然,出于刨根问底的好习惯,我们来详解一下EventBus的源码。
看源码就像拆线球,要先找到一个线头,然后顺着将整条线拉出来。这一次我们就按着EventBus的基本使用来看源码,EventBus.getDefault().register(Object object)就是我们的线头。
先看 getDefault()方法:
|
|
显而易见,这是一个单例模式,使用的是双层锁检测的线程安全的懒汉加载模式的单例(十分长的定语了,后面准备写一篇单例的博文),在第二层是否为空的检测后,对默认单例对象进行了实例化。
这里有个需要注意的地方,我们找到defaultInstance的定义处,看到如下代码
|
|
定义为static自然是为了单例模式的使用,这里要说的是volatile关键字,在对对象进行实例化的时候一般要进行三步:
- 为对象分配空间
- 构造对象
- 将引用指向对象
而在实例化对象时,步骤2和步骤3的顺序是不确定的,也就是说实例化的顺序可能123或132,如果某次实例化的顺序是132,在进行完步骤3、但是还没进行步骤2时有第二条线程访问这个方法,则此时引用defaultInstance已经不为null(已经将这个引用指向了分配好的空间,但是对象还没开始构造),所以在经过第一个if判断语句时,判断不为null,则直接跳到最后将这个还没有构造好对象内容的引用返回到使用单例的地方,就会引起报错。
volatile关键字相当于一个简易的锁,对该引用注明volatile后,在该对象完成初始化之前不会有第二条线程访问到该对象,也就是说不管是123还是132,其他线程都要等这三步完成之后才可以使用这个对象。
单例分析完成,继续看源码,我们点进初始化方法:
|
|
可以看到,在无参的构造方法里调用了另一个有参的构造方法,继续看下去:
|
|
可以看出,这里使用了Builder模式,传入的builder对象是默认的builder对象,方法里还对类里声明的map对象和poster对象进行了初始化,具体意义可以看上面源码中写好的注释。
到这里EventBus的初始化完成,我们已经拿到了一个内部属性都初始化完毕的单例对象,下面顺着线头走,我们来看register方法:
|
|
方法中通过getClass方法获取到传入的Object的class对象subscriberClass ,然后通过之前在构造方法里初始化好的subscriberMethodFinder对象调用了findSubscriberMethods(Class<?> subscriberClass)方法,将subscriberClass 作为参数传了进去。
在看findSubscriberMethods方法的源码之前,我们先回过头看看之前构造方法里对subscriberMethodFinder对象所做的初始化:
|
|
可以看到,这里将用于EventBus对象初始化的builder对象的三个属性作为参数传进了SubscriberMethodFinder类的构造方法中,我们看一下builder的这三个参数。
依次点进去EventBusBuilder类,很容易看到这三个属性的初值:
|
|
由于这里我们使用的getDefault()方法获取的单例,用来初始化EventBus对象的是默认的EventBusBuilder对象,所以builder对象所有的属性值都是默认值,所以这里subscriberInfoIndexes值为null,后面两个boolean值为false,因此被初始化的SubscriberMethodFinder类中对应的这三个属性值也是null、false、false。这些值后面会用到。
看完了subscriberMethodFinder 的初始化,我们再回到主线,来看看findSubscriberMethods方法的源码:
|
|
方法中首先访问了一下METHOD_CACHE,从名字不难看出这是用来缓存的变量,我们点到定义处看这个变量:
|
|
是一个通过class对象查找订阅方法集合的缓存变量,这里我们作为第一次使用这个变量来说,此时从这个缓存里获取到的subscriberMethods 当然为null,如果不是第一次使用这个变量,而是已经有缓存在这个变量中,则subscriberMethods 不为空,直接在后面的if语句判断中被返回。
我们来看subscriberMethods 为空,即还没有缓存的时候,下面有一串if else语句,第一个if语句就用到了之前看到初始化这个类对象时看到的属性ignoreGeneratedIndex,此时ignoreGeneratedIndex为false,所以进入else语句,调用findUsingInfo(subscriberClass)方法为subscriberMethods赋值。
点进findUsingInfo(subscriberClass)方法之前,我们先来看看这个if-else语句中的这两个方法,可以看到,如果ignoreGeneratedIndex为true,则会调用findUsingReflection(subscriberClass)为subscriberMethods赋值,这两个方法有什么区别呢?从名字上看,findUsingReflection(subscriberClass)是使用反射,而findUsingInfo(subscriberClass)是使用什么呢?是使用了apt,使用apt可以在编译的时候就将订阅方法都找到,而不是在运行时找订阅方法,效率提高很多倍,但是需要添加除EventBus之外的另外一个依赖。
这时候可能有人奇怪了,我们之前并没有添加可以使用apt的依赖,怎么前面会调用findUsingInfo(subscriberClass)方法呢?不要慌,继续往下看。
我们点进findUsingInfo(subscriberClass)方法:
|
|
方法内一开始就用了一个之前没见过的类FindState ,我们点进去看一下,可以看到List
先不管那个类的其他属性和方法,回来这个方法,可以看到调用了prepareFindState()方法对findState对象进行赋值,我们看一下这个方法:
|
|
方法中有一个加锁的代码块,点进锁的参数FIND_STATE_POOL,可以看到这是一个FindState数组,长度是4,代码块中有一个for循环,依次从这个数组中取值,取到为null的就进行下一次循环,取到不为null的赋值给局部变量state 之后就将赋值的数组项引用赋值为null,然后将state返回。在4个长度的数组都被用完后,就会直接返回一个new对象。
结合变量的命名,这个过程可以叙述为:该类初始化时会初始化一个大小为4的FindState池,prepareFindState() 的作用就是依次从变量池中取地址返回,被取过的数组地址就变为null,以便下次循环时跳过这个被使用的地址返回下一个还没使用的地址。
可能会有人感到奇怪了,一个类对象的初始化为什么要搞得这么复杂,事实上这是作者一个十分精妙的设计,后面会有地方呼应,我们先继续往下看。
通过prepareFindState()方法,我们的findState对象得到了内存,然后调用了initForSubscriber(subscriberClass)方法,老规矩,看源码:
|
|
是一个很简单的用于初始化属性的方法,注意留意这几个被赋初值的变量,后面会用到。
继续回到 findUsingInfo方法,到现在findState对象的属性值已经全部赋值完毕了,我们继续往下看,下面是一个while循环,判断条件是findState.clazz != null,我们立刻看到了这个熟悉的clazz,这是上一句代码中在初始化方法里被赋值的属性,这个属性在之前的初始化中被赋值subscriberClass,这里的subscriberClass还是之前register传入的object的class对象,自然不为空,所以进入循环。
继续往下走,可以看到调用了getSubscriberInfo(findState)对findState的subscriberInfo赋值,我们看一下getSubscriberInfo方法:
|
|
方法里有一堆if判断,但其实都是纸老虎,我们仔细看一下第一句if判断的条件findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null,在先前对findState的初始化中,subscriberInfo被赋的初值就是null(真的有说后面会用到这几个值,没记到的翻回去找一下然后面壁五分钟),所以第一坨if语句是进不去的。
再看第二坨if语句,判断语句是subscriberInfoIndexes != null,subscriberInfoIndexes 是什么呢?点一下看看,然后在SubscriberMethodFinder类的构造函数中看到了它
|
|
有木有很眼熟?我们在EventBus的初始化方法里用到这货啊:
|
|
再看看subscriberInfoIndexes在这里是什么,由于我们使用的是getDefault拿到的默认单例,所以subscriberInfoIndexes是null,所以第二坨if语句也进不去,直接跳到最后返回null。
一番波折之后,再次回到主线,可以看到返回的null被赋值给了findState的subscriberInfo对象,再下面的if语句马上对这个subscriberInfo对象进行了判断,因为subscriberInfo为空,所以跳过if语句进入了else语句,然后看看else语句里是什么?
|
|
惊不惊喜意不意外?虽然看起来是调用了findUsingInfo方法,但还是调用了使用反射的方法,不加依赖就是不让你用apt。
这之后的步骤和使用反射的findUsingReflection方法后面是相同的,我们来看一下findUsingReflection方法:
|
|
同样使用prepareFindState()对FindState分配内存,调用initForSubscriber方法对FindState对象的属性赋值,对findState的clazz判空后进入while循环,后面的步骤就和findUsingInfo相同了。
接下来就看看findUsingReflection和findUsingInfo两个方法在不加apt依赖的情况下都会运行到的方法 findUsingReflectionInSingleClass:
|
|
方法体很复杂,我们一步步来。
首先通过反射获取方法集合,这里调用反射的是findState.clazz,根据之前分析的,这个对象被赋的值是register的时候传入的Object的class对象,所以这里可以获得注册时传入的订阅者中的所有方法组成的数组。
之后就是对这个方法数组的遍历,通过每个method对象的getModifiers(),判断method是否为public、是否为static或abstract方法,如果是public方法且不是static或abstract方法,则进入第一层if语句,否则报错提示必须为public、且不能是static和abstract方法。
这个语句中通过反射拿到了该方法的参数类型组成的数组,然后判断该数组的长度,如果数组长度是1则进入第二层if语句,否则报错提示方法必须精确地只有一个参数。不知道阅读之前两篇博客的筒子们有没有好奇过接收事件的方法里为什么不能无参或多个参数,这里从源码层面做出了解答。
进入第二层if语句后,通过调用method方法的getAnnotation(Subscribe.class)方法获取到方法的注解对象,然后对这个注解对象进行判空,如果不为空,则证明当前遍历到的方法为用于接收事件的注解方法,于是进入第三层if语句。
第三层if语句中,通过对数组的第一个元素取值拿到了方法的参数类型,也就是传递的事件类型eventType,将当前遍历到的method和获取到的eventType作为参数传入findState对象的checkAdd方法进行判断,返回true则进入第四层if语句。
checkAdd是FindState类中之前我们没搞清楚用处的方法之一,现在根据这里使用它的位置和它的命名就可以大概了解到,checkAdd是一个用于检查的方法,我们点进去看一下:
|
|
方法里用到了anyMethodByEventType,这是一个用HashMap实例化的map对象,可以用EventType查找订阅方法。
方法里首先调用了anyMethodByEventType的put方法,将参数传入的method和eventType传了进去。我们知道hashMap的put方法添加某个key-value值时,如果以前就存在相同的key,则会将用新的value值覆盖相同key的旧的value值,并将旧的value返回;而如果hashmap中原先没有这个key,则会将这个key-value存入hashmap并返回null。所以第一个if判断put方法返回值赋值的existing是否为null,为null则说明以前没有添加过这个eventType,直接返回true。
而如果existing不为null,则说明之前已经添加过这个eventType,代码进入else语句,判断如果existing是Method的子类,则进入else中第一个if语句,然后将被覆盖的老方法传入checkAddWithMethodSignature,开始对方法签名进行检查。
|
|
方法签名的构成就是方法体的前三行,通过将旧方法的方法签名和方法的class对象put进subscriberClassByMethodKey的返回值判断是否已经有了这个接收事件的方法,如果没有则返回true,如果方法重复了则再将原先因为put操作被覆盖的方法class对象put回去,然后返回false。如果这个方法返回false,则checkAdd方法抛出异常,返回true则将当前对象put进anyMethodByEventType,消耗掉对应eventType的value。最后将新传进checkAdd方法的方法传入checkAddWithMethodSignature方法,根据put进subscriberClassByMethodKey后的返回值判断是否已经有了这个方法。
晕了吗,反正我是转了好久才转出来。。。
其实简单的说,checkAdd方法就是在将订阅方法add进findState的方法集合中之前,对方法是否重复进行了一个检查,检查分了两层,第一层就是检查EventType-Method的map集合中是否有EventType,如果连这个EventType都没有这个方法就肯定没有重复;如果有重复的EventType方法也不一定是重复的啊,可能是不同的threadMode同一个EventType的方法啊,所以如果EventType相同就会进入第二层检查,即检查方法签名,方法签名就是由方法名和”>”和eventType的类名构成,确定方法签名不同之后就会返回true,也就表示经过检查后可以将该方法添加到findState的方法集合中。
由此也看出来,我们不能定义一个方法名和事件类相同的接收事件的方法,当然你也定义不了,编译器都会直接给你报错。
回到调用checkAdd的地方,在checkAdd返回true,也就是经检查确定可以将该方法添加到方法集合中后,通过当前遍历到的方法的注解获取到threadMode,然后将当前方法和threadCode、priority、sticky传入订阅方法的集合,代码如下:
|
|
于是findUsingReflectionInSingleClass方法全部运行完成,可以看到整个方法非常复杂,总结起来就是先获取订阅者所有的方法,然后对方法进行遍历,取出public、非静态、非抽象的方法,再取出只有一个参数的方法,再取出有Subscribe注解的方法(总之就是获取到我们使用EventBus时写的@Subscribe注解的接收事件的方法),然后对方法是否重复做一个检测,最后将不重复的方法添加到做状态缓存的findState中的方法集合中。
接下来的moveToSuperclass,是检测到如果当前类还有父类,父类中还有订阅方法,则将findState的clazz指向父类的class对象,然后重复之前获取订阅方法的过程。
最后看一个getMethodsAndRelease方法,方法传入了findState做参数,返回一个订阅方法的集合,是获取所有订阅方法的最后一步。
|
|
方法中用局部变量subscriberMethods 获取到findState中缓存的subscriberMethods,然后调用了findState的recycle方法,点进去不难发现,这是findState用来释放资源的方法。接下来是一个加锁的代码块,我们再次见到了FIND_STATE_POOL,这个4单位大小的FindState数组对象,然后又是一个for循环,但是这跟之前对FIND_STATE_POOL的加锁代码块中的for循环不一样了,我们再把之前的加锁代码块拿过来对比一下,那是在prepareFindState方法中:
|
|
可以看到,之前是从开头开始循环,如果检测到不为null的findState对象,就将这个对象返回赋值给调用该方法处的引用,然后将数组中的这个对象置空;而在getMethodsAndRelease方法中,同样是从开头开始对该数组遍历,然后如果检测到一个数组元素为null,则说明之前这个元素被赋值给某个findState对象了,则再这个方法中将已经释放了资源恢复默认追的findState再次赋值给为null的数组元素。这个过程就是对内存的复用,在4个findState对象之内,一直是在循环利用FIND_STATE_POOL中的内存空间。
将内存返回给FIND_STATE_POOL后,该方法就将局部变量暂存的方法集合返回,一路上溯,方法集合返回到了findSubscriberMethods方法的subscriberMethods中,最后对subscriberMethods做了一个是否为空的判断,如果为空则抛出异常,不为空则将这个订阅类和订阅方法作为key-value保存进METHOD_CACHE中,最后将subscriberMethods返回,于是终于又回到了EventBus类的register方法中,接下来就是对获取到的订阅方法进行一个遍历,然后将每一个方法与订阅者进行subscribe操作。
|
|
作为register的最后一步,方法体略微有些复杂,总结来说就是将订阅者和订阅方法封装进Subscription 对象newSubscription,subscriptionsByEventType是一个能够通过EventType查找Subscription集合的map缓存,将eventType和subscription作为key-value添加进去,避免数据重复;然后通过for循环中对priority的判断,使用优先级将前面封装的newSubscription放在对应的位置。typesBySubscriber是一个可根据订阅者查找到EventType集合的map集合,通过subscriber获取到订阅事件集合后,将subscribe方法传进来的subscriberMethod的eventType添加进订阅事件集合,普通事件的订阅就结束了。
再下面是对黏性事件的处理,如果是黏性事件,则取出该黏性事件,然后post给当前订阅者处理。
至此,register流程就走完了。