前文
由于EventBus的源码还是比较的复杂的,打算使用五篇文章从浅入深的分析它的源码。包括:
- post流程的分析
- register流程分析(反射方式)
- register流程分析(注解方式)【本文】
- stickey消息实现和处理
- 最佳实践
反射的register流程
本文会讲解内容
- 注解的配置,如何才能使用EventBus的注解功能避免耗时。
- 注解的解析流程和代码生成流程
- 注解的获取
注解的配置
- 在使用到
@Subscribe
注解的模块的build.gradle中,引入注解
1 | annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.3.1' |
- 配置下方代码
1 | android { |
其中eventBusIndex
是key,他的值就是我们自动生成的类名,该类中保活了本模块的所有的注解方法,我们下面再说如何生成的该类,verbose
为true就注解可以输出注解过程的日志,我们的每个lib
模块假如需要使用EventBus
就都需要引入对应的配置去生成这样的Index
文件。
然后,我们通过EventBusBulder
去把生成的ModeluNameEventBusIndex
写入,例如
1 | private val eventBus = EventBus.builder() |
后续,我们就可以用这个EventBus
的实例进行register
,那么他就不会耗时去通过反射查找注解方法了。
Index生成
这部分的代码是在源码的EventBusAnnotationProcessor
模块中,EventBusAnnotationProcessor
,即在编译期生成我们各种Index
类的代码就写在这里。他继承自AbstractProcessor
,Java通过注解生成代码的流程我们本文暂不涉及,我们只去看EventBusAnnotationProcessor
具体的实现。process()
方法是开始处理注解的地方
- 读取
eventBusIndex
的值,得到需要生成的类,不存在就return - 调用
collectSubscribers
方法,把得到的ExecutableElement
(描述方法的元素)存入methodsByClass
中,key为所在类,value是方法 - 调用
checkForSubscribersToSkip
查找需要跳过的类- isVisible(),如果对应的
ExecutableElement
对应eventBusIndex
的值所在包名是可访问的,就返回trye,否则是false - isVisible()返回了false,
classesToSkip
就添加一个TypeElement
- isVisible(),如果对应的
- 调用
createInfoIndexFile
,最终创建我们Index文件,他会跳过classesToSkip
中的元素 - 把我们的Index类写入到EventbBuilder中。
注意:假如方法不是使用了public
修饰的,使用了static
修饰的,参数长度不是1的,都会在编译期间报错。抽象方法使用了@Subscribe
注解+Public
修饰,也会假如到Index
类中,接口的方法也是如此的,抽象类和接口都需要是Public
的
通过这样的一个编译期的处理,得到的一个文件的demo是类似于
1 | package com.example.model; |
我们就把ModeluNameEventBusIndex
添加到EventBusBuilder
中,他是支持添加多个的。通过该文件我们就可以知道,只需要通过getSubscriberInfo
方法,就能拿到某一个页面(Activity/Fragment)所注册的所有的SimpleSubscriberInfo
对象,里面包含了所拥有的subscribe
方法数组。下面我们说下这个过程。
注解查找
我们通过上一篇文章,应该就能知道register流程分析(反射方式),知道了,在EventBus中的register
方法是通过SubscriberMethodFinder
的findSubscriberMethods()
去查找,查找分为反射查找与注解查找。之前讲解的是反射查找,本文是注解查找,我们就可以直接看到findUsingInfo()
方法,他就是实现注解查找的关键。
1 | private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) { |
整体看下来,会比通过反射去查找简单很多,也会更快一些。通过分析我们得出一些结论:
- 假如我们启用了注解(ignoreGeneratedIndex=false),但是我们的某个模块没有使用注解,这时候他还是通过反射去查找订阅方法的,这时候他只会查找属于自己的那一层的而已。
- 如果一个子类没有复写父类的订阅方法,该方法也是可以被查出,无论父类是否使用注解。
- 子类与父类都有一个相同的订阅方法,那么只有子类的会生效,多个的话可能会造成崩溃
最终,我们完成了通过发射查找和通过注解查找的方式,拿到了subscriberClass
的所有的所属订阅方法(包括父类),然后使用了METHOD_CACHE
进行了强缓存。