EventBus原理与源码解析
一、概述
EventBus是针对Android优化的发布-订阅事件总线,简化了Android组件间的通信。EventBus以其简单易懂、优雅、开销小等优点而备受欢迎。
关于EventBus的基本使用不再详述,网上的资料很多,可以自行学习,或者直接去GitHub上查看基本使用方法。
在讲解源码之前,先说一下EventBus需要关注的点 - EventBus支持的四种线程模式(ThreadMode):
示例:
@Subscribe(threadMode = ThreadMode.POSTING)
public void eventBus(MyEvent myEvent) {
Toast.makeText(this, "呵呵哒", Toast.LENGTH_SHORT).show();
}
a)POSTING(默认):事件在哪个线程发布,就在哪个线程消费,因此要特别注意不要在UI线程进行耗时的操作,否则会ANR;
b)MAIN:事件的消费会在UI线程。因此,不宜进行耗时操作,以免引起ANR。
c)BACKGROUND:如果事件在UI线程产生,那么事件的消费会在单独的子线程中进行。否则,在同一个线程中消费。
d)ASYNC:不管是否在UI线程产生事件,都会在单独的子线程中消费事件。
另外,EventBus还支持粘性事件,即发送一个未注册的粘性事件,注册者会在完成注册之后收到这个粘性事件。
二、原理
在开始解析源码之前,还是那句话,没有一张图解决不了的问题,如果不是那就是两张图。`(*∩_∩*)′
上图是EventBus整体的运行图。事件的发布与事件的消费可能位于一个线程,也可能位于不同的线程。这取决于我们注册消费方法的时候设置的ThreadMode。
每一个线程都有一个与之关联的Queue(通过ThreadLocal办到的),事件被发布到Queue中,循环遍历Queue中的Event,并根据Event查找可以消费该事件的类(MainActivity)与方法(@Subscribe)。最终将事件交给消费方法完成一次完整的发布与消费过程。
技术关键点:Java 反射、ThreadLocal & Queue、单例模式、建造者模式
速记技巧点:EventBus的以反射开始 - 注册、以反射结束 - 事件的消费。
本文目标:解析事件的注册、消费、解注册过程。
总得来说,与之前看过的其他开源库的源码相比,EventBus的源码还是很容易品尝的(前提是你掌握了上文提到的技术关键点)。下面开始喽。
三、源码解析
3.1、构造EventBus
单例模式:
EventBus.getDefault()
无论是事件的注册、解注册、发布,我们都会用到这句代码。 public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
似不似很熟悉,典型的“双重校验锁”模式。
建造者模式:
public EventBus() {
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
logger = builder.getLogger();
subscriptionsByEventType = new HashMap<>();//<eventType,CopyOnWriteArrayList<Subscription>>事件类型和与之对应的消费者集合
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadSupport = builder.getMainThreadSupport();//1
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;//2
backgroundPoster = new BackgroundPoster(this);//3
asyncPoster = new AsyncPoster(this);//4.从1~4是对四种ThreadMode的支持配置
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
logSubscriberExceptions = builder.logSubscriberExceptions;
logNoSubscriberMessages = builder.logNoSubscriberMessages;
sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
throwSubscriberException = builder.throwSubscriberException;
eventInheritance = builder.eventInheritance;
executorService = builder.executorService;//线程池相关
}
看到了么,在EventBus的构造函数中,使用了目前使用广泛的建造者模式。
3.1、注册
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();//1
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);//2
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);//3
}
}
}
1:这个subscriber就是我们使用EventBus.getDefault().register(this);传入的这个this,比如MainActivity.this。
2:通过反射,查找该Subscriber中,通过@Subscribe注解的方法(消费方法)信息,将这些方法信息封装到SubscriberMethod中,封装的内容包括Method对象、ThreadMode、事件类型、优先级、是否粘性等。一个Subscriber可能对应多个事件消费方法,因此他们的关系是1:N(其中N大于等于1)。
3、完成最终的注册过程。
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);//1
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);//2
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);//3
} else {
if (subscriptions.contains(newSubscription)) {//4
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);//5
break;
}
}
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
if (subscriberMethod.sticky) {//6
if (eventInheritance) {
// Existing sticky events of all subclasses of eventType have to be considered.
// Note: Iterating over all events may be inefficient with lots of sticky events,
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
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);
}
}
}
1:将注册者和事件消费方法封装起来,这样做的原因是完成二者的绑定关系。
2:就像上述注册者和事件消费方法是1:N的关系。一个Event与注册者之间也是1:N的关系。因为一个Event可能会被不同的Activity注册。也就是说Event、注册者、事件消费方法的关系是:1:N:M(其中M、N均大于等于1)。
3:注册者(比如MainActivity.this)与事件消费方法(SubscriberMethod)的关系,我们封装到了Subscription(s)中了。而Event和Subscription(s)的关系,我们通过HashMap保存,key为event.class,value即Subscription(s)。
4:已注册的不能重复注册。
5:按照优先级保存Subscription。
6:粘性相关的处理。
--> 通过Event -> 可以找到Subscription(s) 【subscriber 、SubscriberMethod】
3.2、发布与消费
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();//1
List<Object> eventQueue = postingState.eventQueue;//2
eventQueue.add(event);//3:发布
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);//4:关键方法
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
1:currentPostingThreadState是什么?我们先看下定义:
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
看到ThreadLocal了没有,这是实现线程相关(线程隔离)的关键。也就说会为每一个线程生成一个PostingThreadState,这个PostingThreadState是什么? final static class PostingThreadState {
final List<Object> eventQueue = new ArrayList<>();
boolean isPosting;
boolean isMainThread;
Subscription subscription;
Object event;
boolean canceled;
}
eventQueue就是保存发布到当前线程的Event的。
2|3:事件的发布。
4:这个方法是关键的核心方法,用于消费事件。此处代码最终会调用如下代码:
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 {
// temporary: technically not correct as poster not decoupled from subscriber
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:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
看到没有,线程模式(ThreadMode)在此处发挥了作用,根据ThreadMode方式的不同,有不同的处理策略。以默认的POSTING模式为例,其调用了invokeSubscriber方法:
void invokeSubscriber(Subscription subscription, Object event) {
try {
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
} catch (InvocationTargetException e) {
handleSubscriberException(subscription, event, e.getCause());
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unexpected exception", e);
}
}
有没有发现最终通过什么原理完成方法调用的?反射。
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
这句代码是最精髓的一行了。通过这一行可以发现他们之间的关系:
event -> subscription(subscriber + subscribermethod) ->subscribermethod ->method
4、总结
上一篇: 求和家族,不简单
下一篇: day01 HDFS
推荐阅读
-
vue.js响应式原理解析与实现
-
C#委托与事件原理及实例解析
-
jsp基于XML实现用户登录与注册的实例解析(附源码)
-
Okhttp3源码解析(4)-拦截器与设计模式
-
Mybaits 源码解析 (三)----- Mapper接口底层原理(为什么Mapper不用写实现类就能访问到数据库?)
-
持久层Mybatis3底层源码分析,原理解析
-
JavaScript解析机制与闭包原理实例详解
-
【nodejs原理&源码赏析(4)】深度剖析cluster模块源码与node.js多进程(上)
-
荐 【dubbo源码解析】--- dubbo的服务暴露+服务消费(RPC调用)底层原理深入探析
-
SpringBoot整合log4j日志与HashMap的底层原理解析