注释2:判断 subscriberInfoIndexes
是否为空,如果不为空,则遍历所有,找到一个和 订阅者相匹配的 SubscriberInfo返回回去。
那subscriberInfoIndexes
是什么?怎么来的呢?
首先它是一个 List<SubscriberInfoIndex>
类型,它是一个列表,里面放的是 “订阅者索引”。
它在 EventBus的构造方法中被创建:
EventBus(EventBusBuilder builder) {
…
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
…
}
可以看出它是来自于 EventBusBuilder
,而在EventBusBuilder中, 这个对象又是怎么产生的呢,我通过源码,发现它只有一处地方用来操作这个列表:
// EventBusBuidler.java
/** Adds an index generated by EventBus’ annotation preprocessor. */
public EventBusBuilder addIndex(SubscriberInfoIndex index) {
if (subscriberInfoIndexes == null) {
subscriberInfoIndexes = new ArrayList<>();
}
subscriberInfoIndexes.add(index);
return this;
}
通过 addIndex()
将 SubscriberInfoIndex
往这个列表里面加。再往上找,没有了,addIndex()没有出现在任意一处被调用的地方。
也就是说,这个方法,是外部调用的。
它的英文注释我翻译一下:通过EventBus的注解处理器添加一个索引 。
这就说明了这个方法,是由注解器产生的Java文件调用。这也说明了,如果我们不使用注解处理器,EventBus寻找 @Subcriber
的做法,永远是 反射 + 遍历。
2. 通过EventBusAnnotationProcessor生成Java文件
==========================================================================================================
EventBusAnnotationProcessor
需要引入,示例代码如下:
// build.gradle
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
arguments = [ eventBusIndex : ‘com.example.myapp.MyEventBusIndex’ ]
}
}
}
}
dependencies {
def eventbus_version = ‘3.2.0’
implementation “org.greenrobot:eventbus:$eventbus_version”
annotationProcessor “org.greenrobot:eventbus-annotation-processor:$eventbus_version”
}
然后在项目中,写入 @Subscribe
方法:
public class MainActivity extends AppCompatActivity {
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMainActivityEvent(MainActivityEvent event){
}
…
}
点击编译,就会在下图目录中看到编译时生成的文件:
来看下文件内容:
public class MyEventBusIndex implements SubscriberInfoIndex {
private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;
static {
SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>(); // 1
putIndex(new SimpleSubscriberInfo(MainActivity.class, true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo(“onMainActivityEvent”, MainActivityEvent.class, ThreadMode.MAIN),
})); // 2
}
private static void putIndex(SubscriberInfo info) {
SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
}
@Override
public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) { // 3
SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
if (info != null) {
return info;
} else {
return null;
}
}
}
注释1: 在静态代码块中, 创建一个 Map,key是所有的订阅者, value 是该订阅者所有的订阅方法
注释2:对每个订阅者,将其内部所有的 @Subscribe方法添加其 SubscriberMethodInfo[]
这个数组中,并封装到 SubscriberInfo中,put到注释1的Map中。
注释3:getSubscriberInfo()
用来拿到对应订阅者的信息,在第一节中的 SubscriberMethodFiner.getSubscriberInfo()
里面,我们看到的正是调用了这个方法。
但是如果我们默认情况下使用 EventBus.getDefault().register()
是不会调用到这个方法的,因为默认方法并不会 赋值给subscriberInfoIndexes
,所以我们要手动加入:
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus(); // 1
EventBus.getDefault().register(this);
注释1:通过 addIndex()
把生成出来的Java文件加入进去,它会给 subscriberInfoIndexes
赋值。这个方法只能使用一次,建议在Application的onCreate中使用。
到这里,索引的用法就结束了。通过使用索引,把搜索订阅方法的做法放在了编译时做,相比于运行时暴力搜索,性能相比可见一斑。官方给出了在Nexus5上性能图:
EventBus3.0在没有使用注解生成器的性能比2.x都低(我现在就是处在这个位置),但是使用索引之后,速度能快到究极独一档。所以没什么好说的,赶紧用了。
接下来我要分析一下EventBusAnnotationProcessor的源码,这部分不感兴趣的同学可以不看了。
3.EventBusAnnotationProcessor部分源码分析
====================================================================================================
AnnotationProcessor的关键地方都是在与 process()
是怎么生成Java文件的,来看下它的 process()
:
// EventBusAnnotationProcessor.java
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
Messager messager = processingEnv.getMessager(); // 1
try {
String index = processingEnv.getOptions().get(OPTION_EVENT_BUS_INDEX); // 2
…
verbose = Boolean.parseBoolean(processingEnv.getOptions().get(OPTION_VERBOSE)); // 3
int lastPeriod = index.lastIndexOf(‘.’);
String indexPackage = lastPeriod != -1 ? index.substring(0, lastPeriod) : null; // 4
…
collectSubscribers(annotations, env, messager); // 5
checkForSubscribersToSkip(messager, indexPackage); // 6
if (!methodsByClass.isEmpty()) {
createInfoIndexFile(index); // 7
} …
} …
}
process()
仅留下比较关键的代码,其他非关键代码更多是容错相关。
注释1: 获取信使,它用来打印log,因为AnnotationProcessor组件是Java的,所以不能使用Android的Log工具来打印,它就起到这么一个作用。
注释2:OPTION_EVENT_BUS_INDEX
就是我们在 gradle文件中写的 : [eventBusIndex : 'xxx']
的内容,通过注释2的方法,可以拿到当前项目EventBus的 eventBusIndex
。它的作用是用来创建索引文件。
注释3:拿到verbose值,它同样在 gralde中拿,但是我们导包时没有写出来,annotationProcessor的完整导包是:
javaCompileOptions {
annotationProcessorOptions {
arguments = [ eventBusIndex : ‘com.example.myapp.MyEventBusIndex’
verbose : ‘true’]
}
}
这个 verbose如果为true,那我们注释1的信使就会在 编译时打印编译这些代码的log,如果为false则不打印。所以这个字段并不重要,我们一般都会忽略。
注释4:根据注释2拿到的索引,生成一个包名,就是新生成的Java文件的所在包的包名。
注释5:粗略收集所有的订阅者信息,因为订阅者是带有 @Subscribe
注解的文件,所以 AnnotationProcessor能够扫描到。
注释6:检查注释5中的订阅者,如果他们不是public,或者说他们的内部包含的事件方法不是public,则去除掉这些订阅者。
注释7:创建索引文件
上面最终要的方法是注释5、注释7。
先来看下注释5。
// EventBusAnnotationProcessor.java
private final ListMap<TypeElement, ExecutableElement> methodsByClass = new ListMap<>();
private void collectSubscribers(Set<? extends TypeElement> annotations, RoundEnvironment env, Messager messager) {
// 1
for (TypeElement annotation : annotations) {
Set<? extends Element> elements = env.getElementsAnnotatedWith(annotation); // 2
for (Element element : elements) { // 3
if (element instanceof ExecutableElement) {
ExecutableElement method = (ExecutableElement) element; // 4
if (checkHasNoErrors(method, messager)) {
TypeElement classElement = (TypeElement) method.getEnclosingElement(); // 5
methodsByClass.putElement(classElement, method);// 6
}
} else {
messager.printMessage(Diagnostic.Kind.ERROR, “@Subscribe is only valid for methods”, element);
}
}
}
}
注释1: 遍历所有的 annotations,annotations是被AnnotationProcessor扫描项目扫出来的,这个Set只有一个元素,那就是 Subscribe。
注释2:拿到所有被 @Subscribe 标记的 元素(也就是方法)
注释3:遍历所有的 注释2中获取的元素
注释4:把 元素(Element) 转换成 方法(ExecutableElement)
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V:vip204888 备注Android获取(资料价值较高,非无偿)
文末
那么对于想坚持程序员这行的真的就一点希望都没有吗?
其实不然,在互联网的大浪淘沙之下,留下的永远是最优秀的,我们考虑的不是哪个行业差哪个行业难,就逃避掉这些,无论哪个行业,都会有他的问题,但是无论哪个行业都会有站在最顶端的那群人。我们要做的就是努力提升自己,让自己站在最顶端,学历不够那就去读,知识不够那就去学。人之所以为人,不就是有解决问题的能力吗?挡住自己的由于只有自己。
Android希望=技能+面试
- 技能
- 面试技巧+面试题
截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**
如果你觉得这些内容对你有帮助,可以添加V:vip204888 备注Android获取(资料价值较高,非无偿)
[外链图片转存中…(img-ye5Llt1b-1711558291370)]
文末
那么对于想坚持程序员这行的真的就一点希望都没有吗?
其实不然,在互联网的大浪淘沙之下,留下的永远是最优秀的,我们考虑的不是哪个行业差哪个行业难,就逃避掉这些,无论哪个行业,都会有他的问题,但是无论哪个行业都会有站在最顶端的那群人。我们要做的就是努力提升自己,让自己站在最顶端,学历不够那就去读,知识不够那就去学。人之所以为人,不就是有解决问题的能力吗?挡住自己的由于只有自己。
Android希望=技能+面试
- 技能
[外链图片转存中…(img-kOV77pwD-1711558291370)] - 面试技巧+面试题
[外链图片转存中…(img-m3f0n56z-1711558291370)]