🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] 上一篇文章对 EventBus 进行了整体的源码分析,看到 EventBus 3.0 获取订阅事件的注解信息共有两种方式,分别是通过 EventBusAnnotationProcessor 注解处理器和运行时通过反射获取,本文主要对这两种方式进行源码分析,并对编译时注解和运行时注解进行拓展学习。 在 EventBus 的 findSubscriberMethods 方法中可以看到: ```java List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { ... if (ignoreGeneratedIndex) { subscriberMethods = findUsingReflection(subscriberClass); } else { subscriberMethods = findUsingInfo(subscriberClass); } ... ``` 根据 ignoreGeneratedIndex 的值,分别有通过反射和索引两种方式寻找订阅方法,接下来我们一一来看: # 通过反射 findUsingReflection 方法是通过运行时反射来寻找订阅方法的,来看看它的源码: ```java private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) { // 用来做订阅方法的订阅和保存 FindState findState = prepareFindState(); // 初始化观察者的 FindState findState.initForSubscriber(subscriberClass); while (findState.clazz != null) { // 通过反射来寻找订阅方法 findUsingReflectionInSingleClass(findState); // 查找父类的订阅方法 findState.moveToSuperclass(); } // 获得 findState 中的订阅方法 list 并返回 return getMethodsAndRelease(findState); } ``` 接下来看看 findUsingReflectionInSingleClass 方法的源码: ```java private void findUsingReflectionInSingleClass(FindState findState) { Method[] methods; // 通过反射得到方法数组 try { // This is faster than getMethods, especially when subscribers are fat classes like Activities methods = findState.clazz.getDeclaredMethods(); } catch (Throwable th) { // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149 methods = findState.clazz.getMethods(); findState.skipSuperClasses = true; } // 遍历 Method for (Method method : methods) { int modifiers = method.getModifiers(); if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { Class<?>[] parameterTypes = method.getParameterTypes(); // 保证必须只有一个事件参数 if (parameterTypes.length == 1) { // 得到注解 Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class); if (subscribeAnnotation != null) { Class<?> eventType = parameterTypes[0]; // 检查是否添加方法到 findState if (findState.checkAdd(method, eventType)) { ThreadMode threadMode = subscribeAnnotation.threadMode(); // 添加订阅方法到 findState 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"); } } } ``` 至此,通过反射获取到了观察者的订阅方法。 # 通过 EventBusAnnotationProcessor 注解处理器 EventBus 3.0 索引使用方法: 要应用我们生成的索引时,需要在构造 EventBus 时传入我们自定义的 EventBusIndex,如: ```java EventBus mEventBus = EventBus.builder().addIndex(new MyEventBusIndex()).build(); ``` 或者是将索引应用到默认的单例中: ```java EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus(); ``` 接下来我们分析下源码,首先来看看 findUsingInfo 方法: ```java private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) { FindState findState = prepareFindState(); findState.initForSubscriber(subscriberClass); while (findState.clazz != null) { // 从 findState 中获取 SubscriberInfo findState.subscriberInfo = getSubscriberInfo(findState); if (findState.subscriberInfo != null) { // 从 subscriberInfo 中获得订阅方法数组 SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods(); for (SubscriberMethod subscriberMethod : array) { // 检查是否添加方法到 findState if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) { // 添加订阅方法到 findState findState.subscriberMethods.add(subscriberMethod); } } } else { // 通过反射获取订阅方法 findUsingReflectionInSingleClass(findState); } // 查找父类的订阅方法 findState.moveToSuperclass(); } return getMethodsAndRelease(findState); } ``` 看一下关键的 getSubscriberInfo 方法: ```java private SubscriberInfo getSubscriberInfo(FindState findState) { if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) { SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo(); if (findState.clazz == superclassInfo.getSubscriberClass()) { return superclassInfo; } } if (subscriberInfoIndexes != null) { for (SubscriberInfoIndex index : subscriberInfoIndexes) { SubscriberInfo info = index.getSubscriberInfo(findState.clazz); if (info != null) { return info; } } } return null; } ``` # 注解处理器的使用 本段转载自:[Java注解处理器](https://www.race604.com/annotation-processing/) 这篇文章讲解注解处理器非常不错,感兴趣可以仔细阅读。 ## 什么是注解处理器 注解处理器(Annotation Processor)是 javac 的一个工具,它用来在编译时扫描和处理注解(Annotation)。注解处理器以 Java 代码(或者编译过的字节码)作为输入,生成文件(通常是 .java 文件)作为输出。这些生成的 Java 代码是在生成的 .java 文件中,所以你不能修改已经存在的 Java 类,例如向已有的类中添加方法。这些生成的 Java 文件,会同其他普通的手动编写的 Java 源代码一样被 javac 编译。 ## AbstractProcessor 每一个注解处理器都是继承于 AbstractProcessor,如下所示: ```java package com.example; public class MyProcessor extends AbstractProcessor { @Override public synchronized void init(ProcessingEnvironment env){ } @Override public boolean process(Set<? extends TypeElement> annoations, RoundEnvironment env) { } @Override public Set<String> getSupportedAnnotationTypes() { } @Override public SourceVersion getSupportedSourceVersion() { } } ``` * init(ProcessingEnvironment env): 每一个注解处理器类都必须有一个空的构造函数。然而,这里有一个特殊的 init()方法,它会被注解处理工具调用,并输入 ProcessingEnviroment 参数。ProcessingEnviroment 提供很多有用的工具类 Elements, Types 和 Filer。 * process(Set<? extends TypeElement> annotations, RoundEnvironment env): 这相当于每个处理器的主函数 main()。你在这里写你的扫描、评估和处理注解的代码,以及生成 Java 文件。输入参数 RoundEnviroment,可以让你查询出包含特定注解的被注解元素。 * getSupportedAnnotationTypes(): 这里你必须指定,这个注解处理器是注册给哪个注解的。注意,它的返回值是一个字符串的集合,包含本处理器想要处理的注解类型的合法全称。换句话说,你在这里定义你的注解处理器注册到哪些注解上。 * getSupportedSourceVersion(): 用来指定你使用的 Java 版本。通常这里返回 SourceVersion.latestSupported()。然而,如果你有足够的理由只支持 Java 6的话,你也可以返回 SourceVersion.RELEASE_6。我推荐你使用前者。 在 Java 7中,也可以使用注解来代替 getSupportedAnnotationTypes()和 getSupportedSourceVersion(),像这样: ```java @SupportedSourceVersion(SourceVersion.latestSupported()) @SupportedAnnotationTypes({ // 合法注解全名的集合 }) public class MyProcessor extends AbstractProcessor { @Override public synchronized void init(ProcessingEnvironment env){ } @Override public boolean process(Set<? extends TypeElement> annoations, RoundEnvironment env) { } } ``` # 总结 经过一天加一个晚上的时间,EventBus 3.0 的源码分析工作完成了,收获很多。 EventBus 是对于观察者模式一个很好的实践,接下来也要对其他常见设计模式进行系统的学习。注解处理器现在广泛应用于各个开源库,通过这次源码分析,也加深了对于注解的理解。