[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 是对于观察者模式一个很好的实践,接下来也要对其他常见设计模式进行系统的学习。注解处理器现在广泛应用于各个开源库,通过这次源码分析,也加深了对于注解的理解。
- 导读
- Java知识
- Java基本程序设计结构
- 【基础知识】Java基础
- 【源码分析】Okio
- 【源码分析】深入理解i++和++i
- 【专题分析】JVM与GC
- 【面试清单】Java基本程序设计结构
- 对象与类
- 【基础知识】对象与类
- 【专题分析】Java类加载过程
- 【面试清单】对象与类
- 泛型
- 【基础知识】泛型
- 【面试清单】泛型
- 集合
- 【基础知识】集合
- 【源码分析】SparseArray
- 【面试清单】集合
- 多线程
- 【基础知识】多线程
- 【源码分析】ThreadPoolExecutor源码分析
- 【专题分析】volatile关键字
- 【面试清单】多线程
- Java新特性
- 【专题分析】Lambda表达式
- 【专题分析】注解
- 【面试清单】Java新特性
- Effective Java笔记
- Android知识
- Activity
- 【基础知识】Activity
- 【专题分析】运行时权限
- 【专题分析】使用Intent打开三方应用
- 【源码分析】Activity的工作过程
- 【面试清单】Activity
- 架构组件
- 【专题分析】MVC、MVP与MVVM
- 【专题分析】数据绑定
- 【面试清单】架构组件
- 界面
- 【专题分析】自定义View
- 【专题分析】ImageView的ScaleType属性
- 【专题分析】ConstraintLayout 使用
- 【专题分析】搞懂点九图
- 【专题分析】Adapter
- 【源码分析】LayoutInflater
- 【源码分析】ViewStub
- 【源码分析】View三大流程
- 【源码分析】触摸事件分发机制
- 【源码分析】按键事件分发机制
- 【源码分析】Android窗口机制
- 【面试清单】界面
- 动画和过渡
- 【基础知识】动画和过渡
- 【面试清单】动画和过渡
- 图片和图形
- 【专题分析】图片加载
- 【面试清单】图片和图形
- 后台任务
- 应用数据和文件
- 基于网络的内容
- 多线程与多进程
- 【基础知识】多线程与多进程
- 【源码分析】Handler
- 【源码分析】AsyncTask
- 【专题分析】Service
- 【源码分析】Parcelable
- 【专题分析】Binder
- 【源码分析】Messenger
- 【面试清单】多线程与多进程
- 应用优化
- 【专题分析】布局优化
- 【专题分析】绘制优化
- 【专题分析】内存优化
- 【专题分析】启动优化
- 【专题分析】电池优化
- 【专题分析】包大小优化
- 【面试清单】应用优化
- Android新特性
- 【专题分析】状态栏、ActionBar和导航栏
- 【专题分析】应用图标、通知栏适配
- 【专题分析】Android新版本重要变更
- 【专题分析】唯一标识符的最佳做法
- 开源库源码分析
- 【源码分析】BaseRecyclerViewAdapterHelper
- 【源码分析】ButterKnife
- 【源码分析】Dagger2
- 【源码分析】EventBus3(一)
- 【源码分析】EventBus3(二)
- 【源码分析】Glide
- 【源码分析】OkHttp
- 【源码分析】Retrofit
- 其他知识
- Flutter
- 原生开发与跨平台开发
- 整体归纳
- 状态及状态管理
- 零碎知识点
- 添加Flutter到现有应用
- Git知识
- Git命令
- .gitignore文件
- 设计模式
- 创建型模式
- 结构型模式
- 行为型模式
- RxJava
- 基础
- Linux知识
- 环境变量
- Linux命令
- ADB命令
- 算法
- 常见数据结构及实现
- 数组
- 排序算法
- 链表
- 二叉树
- 栈和队列
- 算法时间复杂度
- 常见算法思想
- 其他技术
- 正则表达式
- 编码格式
- HTTP与HTTPS
- 【面试清单】其他知识
- 开发归纳
- Android零碎问题
- 其他零碎问题
- 开发思路