EventBus是一款用于传递事件的开源框架,首次使用就被其极低的耦合性给折服,同时它也支持订阅方法的线程指定。最近阅读了它的源码,觉得内部设计流程还是非常值得我们借鉴的,特此出一篇文章分享给大家。
源码分析基于EventBus3.1.1
1. EventBus使用
EventBus使用极其简单,大概可分为四小步:
- 定义事件类:定义一个任意事件类
EventType
,用于存储需要发送的内容。 - 注册:在合适的时机注册
EventBus
,比如在Activity
的onCreate()
,同时为了避免造成性能的浪费要在onDestroy()
中解除注册。 - 接收事件:在注册类中写一个
x x x(eventMessage)
方法,一定要被public
修饰喝subscribe
注解。注册后可以接收所有通过EventBus发送的eventMessage
类型事件 - 发送事件:可以在任意地方
EventBus.getDefault().post(obj)
发送事件。
具体代码:
//订阅
EventBus.getDefault().register(this);
/订阅方法,用于接收事件,注解的三个参数:线程类型,是否为粘性事件,优先级
@Subscribe(thread, sticky , priority)
public onEvent(EventMessage eventMessage){
}
//解除订阅
EventBus.getDefault().unregister(this);
//发送事件
EventBus.getDefault().post(eventMessage)
使用很简单,发送方可以在任意位置任意事件进行消息发送,耦合度极低,这也是EventBus优势所在。
2. 源码解析
2.1 注册
首先来看EventBus.getDefault()源码:
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
static volatile EventBus defaultInstance;
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
public EventBus() {
this(DEFAULT_BUILDER);
}
//通过建造者模式构造EventBus对象
EventBus(EventBusBuilder builder) {
....
}
标准的单例模式。EventBus中内置了几个集合,如订阅方法集,需要全局保存,另外发送消息也需要EventBus
对象,每发送一个消息就定义一个对象显然是不可取的,所以定义为单例再合适不过了。实例化借助EventBusBuilder
结合建造者模式实现,一些重要参数也是在此过程创建,后面会逐一进行描述。
再来看register部分代码:
public void register(Object subscriber) {
//获取订阅对象的class对象
Class<?> subscriberClass = subscriber.getClass();
//获取订阅方法
List<SubscriberMethod> subscriberMethods =
subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
//遍历方法集,逐个进行subscribe()操作
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber,iberMethod);
}
}
}
- 获取订阅对象class对象
- 通过subscriberMethodFinder获取到订阅对象中订阅方法集合。
- 对获取到的方法逐个进行subscribe操作。
BubscriberMethodFinder
在EventBus构造方法中实例化,用来管理订阅者和订阅方法,通过findSubscriberMethods()
方法获取到订阅者中所有订阅方法,源代码如下:
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//首先从缓存中取方法集
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//ignoreGeneratedIndex默认值为false
if (ignoreGeneratedIndex) {
//通过插件生成MyEventBusIndex类获取
subscriberMethods = findUsingReflection(subscriberClass);
} else {
//通过反射获取
subscriberMethods = findUsingInfo(subscriberClass);
}
//获取到的方法集为空抛出异常
if (subscriberMethods.isEmpty()) {
throw new EventBusException("xxx");
} else {
//将获取到的方法集加入到缓存
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
代码中可以看到,获取订阅方法集有两种方式,一种通过插件生成的代码来获取,另一种通过反射,本篇文章着重研究反射方式实现。插件生成代码这一块跟Butterknife类似,感兴趣的同学可以自行了解。
findUsingInfo(subscriberClass)
源码如下:
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
//获取FindState对象
FindState findState = prepareFindState();
//初始化FindState
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
//获取订阅者详情
findState.subscriberInfo = getSubscriberInfo(findState);
//如果使用了MyEventBusIndex会从此处获取订阅方法
if (findState.subscriberInfo != null) {
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array) {
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
//通过反射获取到订阅方法
findUsingReflectionInSingleClass(findState);
}
// 获得其父类
findState.moveToSuperclass();
}
//返回当前订阅者中所有订阅方法
return getMethodsAndRelease(findState);
}
获取订阅方法的过程大概可分为四步:
- 获取FindState对象并进行初始化
- 从订阅类开始,遍历至顶层父类获取订阅方法
- 获取订阅者详情,判断获取订阅方法的方式。共有两种,一种用过插件生成代码,一种通过反射,本篇文章
- 返回订阅方法并释放资源
下面我来详细解析这四部分内容
1.初始化FindState
FindState
是SubscriberMethodFinder
的一个内部类,用来管理和存储订阅方法集合,通过prepareFindState()
获取initForSubscriber()
初始化,代码如下:
private FindState prepareFindState() {
synchronized (FIND_STATE_POOL) {
//POOL_SIZE为缓存池长度,默认为4
for (int i = 0; i < POOL_SIZE; i++) {
FindState state = FIND_STATE_POOL[i];
//如果缓存池中存在FindState对象
if (state != null) {
//从数组中取出并返回
FIND_STATE_POOL[i] = null;
return state;
}
}
}
//缓存池为空直接new一个
return new FindState();
}
void initForSubscriber(Class<?> subscriberClass) {
this.subscriberClass = clazz = subscriberClass;
skipSuperClasses = false;
subscriberInfo = null;
}
SubscriberMethodFinder
内部维护了一个FindState
缓存池,避免创建大量FindState
对象造成内存浪费。初始化阶段对订阅类的Class对象进行保存以及一些变量的重置。
2. 遍历父类获取订阅方法
while (findState.clazz != null) {
...
findState.moveToSuperclass();
}
void moveToSuperclass() {
if (skipSuperClasses) {
clazz = null;
} else {
clazz = clazz.getSuperclass();
...
}
}
通过调用moveToSuperclass()
获取findState父类,并将引用传递给其clazz变量。当遍历至最顶层父类时findState.clazz为null跳出while循环。
3. 获取订阅方法
此步骤是EventBus订阅的核心,在该流程会通过MyEventBusIndex
或者findUsingReflectionInSingleClass ()
获取到订阅方法,本篇文章只对第二种进行详细分析。
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
//1
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
methods = findState.clazz.getMethods();
findState.skipSuperClasses = true;
}
//2
for (Method method : methods) {
//获取方法修饰符
int modifiers = method.getModifiers();
//3
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
//获取方法中所有参数的Class对象
Class<?>[] parameterTypes = method.getParameterTypes();
//4
if (parameterTypes.length == 1) {
//5
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
Class<?> eventType = parameterTypes[0];
if (findState.checkAdd(method, eventType)) {
//获取注解中线程类型
ThreadMode threadMode = subscribeAnnotation.threadMode();
//构造SubscriberMethod()进行添加
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
...
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
...
}
}
}
流程还是漫长的,下面为根据注释中的标注逐个进行解析:
1:通过反射获取到订阅者中所有的方法。
2:遍历订阅者所有方法
3:获取当前方法修饰符是否为public
4:获取方法中所有参数的Class
对象,并判断length()
是否为1
即参数是否为1
个。
5:获取方法注解对象,并且获取到注解中线程类型
,优先级
,是否粘性
,最终结合method
,eventType
构造成SubscriberMethod
添加到findState的subscriberMethods中。
4. 返回订阅方法并释放资源
执行完findUsingReflectionInSingleClass ()
后会通过getMethodsAndRelease(findState)
获取到订阅方法集合并return。
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
//状态重置
findState.recycle();
synchronized (FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
if (FIND_STATE_POOL[i] == null) {
//存入缓存
FIND_STATE_POOL[i] = findState;
break;
}
}
}
return subscriberMethods;
}
首先创建一个ArrayList
集合,将上一步获取到的订阅方法集合存入,随后将findState
对象重置状态并放入到缓冲池FIND_STATE_POOL
中。
再回到register()
处,获取完订阅方法后会逐个遍历进行 subscribe()
操作
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//1
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
//2
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//3
if (subscriberMethod.sticky) {
if (eventInheritance) {
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
for (Map.Entry<Class<?>, Object> entry : entries) {
Class<?> candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
首先介绍一下用到了两个Map集合
- subscriptionsByEventType:映射关系是
EventType
到CopyOnWriteArrayList<Subscription>
(用于存储所有EventType对应的所有Subscription对象),Subscription
内部包含订阅者
和订阅方法封装类SubscriberMethod
。- typesBySubscriber:映射关系是
订阅者
到List<Class<?>>>
,List中的Class对象即EventType
(订阅方法中参数类型)的Class对象。
订阅流程大致可分为三大步
- 第一步:首先获取到订阅方法参数类型的Class对象
eventType
,封装一个Subscription
对象newSubscription
,然后根据eventType
从subscriptionsByEventType
获取到subscriptions
集合,如果为空则创建并添加到subscriptionsByEventType
中。随后遍历subscriptions
集合根据优先级判断将newSubscription
插入到相应位置。 - 第二步:根据
订阅者对象
从typesBySubscriber
获取到当前订阅者中EventType
的Class对象集合subscribedEvents
,如果为空则创建并添加到typesBySubscriber
中,最后将eventType
添加到subscribedEvents
集合中。 - 第三步:判断是否为粘性事件,如果是就会调用
checkPostStickyEventToSubscription(newSubscription, stickyEvent)
,机制与post()
类似。
解除注册
解除注册相比于注册就要简单的多,只需将订阅者以及其订阅方法从相应的集合移除即可,代码如下:
public synchronized void unregister(Object subscriber) {
//获取EventType
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
//解绑
unsubscribeByEventType(subscriber, eventType);
}
//从Map集合中删除
typesBySubscriber.remove(subscriber);
} else {
...
}
}
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
//获取到eventType对应的集合
List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions != null) {
int size = subscriptions.size();
for (int i = 0; i < size; i++) {
Subscription subscription = subscriptions.get(i);
//如果订阅者与当前订阅者相同
if (subscription.subscriber == subscriber) {
//进行移除操作
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
大致也可分为三步:
- 首先根据订阅者获取到所有EventType的Class对象,
- 逐个遍历执行
unsubscribeByEventType(subscriber, eventType)
解绑操作,具体步骤注释写的很清楚就不多赘述。 - 将订阅者
subscriber
从typesBySubscriber中删除。
2.2 通知
发送通知从post(eventType)开始,来看代码:
public void post(Object event) {
//获取到当前线程的PostingThreadState对象
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
//将event加入到队列中
eventQueue.add(event);
//判断事件是否已经发送
if (!postingState.isPosting) {
//当前线程是否为主线程
postingState.isMainThread = isMainThread();
postingState.isPosting = true;
//事件是否取消发送
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
//逐个将队列中的消息进行发送,发送完毕进行移除操作
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
final static class PostingThreadState {
//当前线程未被发送的事件队列
final List<Object> eventQueue = new ArrayList<>();
//是否已经发送
boolean isPosting;
//当前线程是否为主线程
boolean isMainThread;
Subscription subscription;
Object event;
//是否取消发送
boolean canceled;
}
- 首先通过
currentPostingThreadState
(是一个ThreadLocal)获取到当前线程的PostingThreadState
对象。PostingThreadState
内部保存了当前线程的一些信息如当前线程 - 获取到当前事件队列
eventQueue
,然后将事件event
加入到队列中。 - 遍历队列
eventQueue
逐个执行postSingleEvent()
方法。
postSingleEvent()
代码如下:
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
//1
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
//2
if (eventInheritance) {
//3
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {//2
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
if (!subscriptionFound) {
//错误日志
}
}
代码中流程大概可分为三个步骤:
- 获取
event
的Class对象。 - 判断
eventInheritance
(默认为true)是否发送父类事件。 - 获取
event
父类对象集合,遍历父类集合逐个执行postSingleEventForEventType()
方法,
来看一下postSingleEventForEventType()`方法代码:
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
//根据eventClass获取到所有对应的Subscription对象
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
//遍历subscriptions
for (Subscription subscription : subscriptions) {
//将subscription 中的值赋予postingState
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
postToSubscription(subscription, event, postingState.isMainThread);
aborted = postingState.canceled;
} finally {
//发生异常重置状态
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) {
break;
}
}
return true;
}
return false;
}
首先根据eventClass
获取到集合subscriptions
,随后遍历逐个将值赋给postingState
,最后调用postToSubscription()
方法执行发布操作。下面是postToSubscription()
代码:
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
invokeSubscriber(subscription, event);
break;
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case MAIN_ORDERED:
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case BACKGROUND:
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case ASYNC:
asyncPoster.enqueue(subscription, event);
break;
default:
...
}
}
void invokeSubscriber(Subscription subscription, Object event) {
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
...
}
postToSubscription()
会根据不同的threadMode
类型执行不同的发布策略,下面我来解释一下几种threadMode
区别:
- POSTING:直接调用
invokeSubscriber()
方法- MAIN:判断当前线程是否位于主线程,如果是直接执行
invokeSubscriber()
方法,否则将事件消息加入到mainThreadPoster
队列,最终交由Handler
处理。- MAIN_ORDERED:如果
mainThreadPoster
不为空直接将事件消息加入到mainThreadPoster
中,否则调用invokeSubscriber()
方法。- BACKGROUND:如果位于主线程直接将事件加入到队列
backgroundPoster
中,最后交由线程池在工作线程中处理。否则直接调用invokeSubscriber()
方法。- ASYNC:与
BACKGROUND
类似,区别是ASYNC
会直接将事件加入到asyncPoster
队列中,最终交给工作线程处理。
invokeSubscriber()
方法中会直接通过反射调用订阅方法,至此EventBus工作流程分析完毕。
参考书籍:《Android进阶之光》
总结
整体来讲EventBus源码阅读还是比较简单的,内部存在三个核心点:单例定义EventBus
来全局管理事件、订阅者以及订阅方法,基于观察者模式来维持订阅者与通知者关系,通过反射调用订阅方法并传递事件。理清以上三个关键点读懂EventBus
源码并不难。
每一次阅读优秀框架源码都能感受到作者强大的设计能力,总是能将设计模式应用的恰到好处。就作者本人而言,设计模式在嘴里面也念叨了好几年,更多的时候是为了用设计模式而用设计模式,通过本次源码阅读,作者对观察者模式又增加了些不同看法。所以有意向深入了解设计模式的同学,我强烈推荐结合源码去学习。