EventBus3.0源码分析(一)事件注册与发布流程
引言
EventBus是一个Android事件发布/订阅框架,通过解耦发布者和订阅者简化 Android 事件传递,可以实现线程与Android组件之间的通信。EventBus使用简单,并将事件发布和订阅充分解耦,从而使代码更简洁。目前网上已经有很多优秀的分析它源码的文章,这里主要是为了自己深入理解学习,做备忘用。
简单使用
1.Gradle配置:
implementation 'org.greenrobot:eventbus:3.1.1'
2.构造消息实体类:
package com.qicode.kakaxicm.enventbusdr;
/**
* Created by chenming on 2018/7/17
*/
public class MsgEvent {
String value;
public MsgEvent(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
3.注册和注销订阅者
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);//注册订阅者
}
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);//注销订阅者
}
}
3.定义事件接受者方法:
@Subscribe(threadMode = ThreadMode.MAIN,sticky = true, priority = 100)
public void onMessageEvent(MsgEvent event) {
Toast.makeText(this, event.getValue(),Toast.LENGTH_LONG).show();
}
这里的Subscribe注解描述了这个方法的运行线程,是否是粘性事件和优先级,关于这个注解,后面会分析。onMessageEvent方法运行在主线程,非粘性,优先级100.
注意事件接收方法为public,针对同一事件可以定义多个接收方法,如下:
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true, priority = 100)
public void onMessageEvent(MsgEvent event) {
Toast.makeText(this, event.getValue(), Toast.LENGTH_LONG).show();
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true, priority = 101)
public void onMessageEvent1(MsgEvent event) {
Toast.makeText(this, event.getValue()+"11111", Toast.LENGTH_LONG).show();
}
onMessageEvent 和onMessageEvent1都可以接收到MsgEvent,后者方法优先级跟高,会先执行。
4.发送事件:
/**
* 发送事件
* @param view
*/
public void onSend(View view) {
//构造事件消息,发送
MsgEvent event = new MsgEvent("haha");
EventBus.getDefault().post(event);
}
可以发现事件接收方法可以通过注解灵活配置,与传统的Handler发送消息相比,代码简洁,维护方便。当然了,EventBus的内部订阅事件和接收事件的线程切换也是通过Handler处理的。
EventBus源码分析
创建EventBus
//defaultInstance为单例
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
下面我们看看主要属性:
static volatile EventBus defaultInstance;
//默认构造器
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
//key为事件类型,value为订阅了该类型事件的订阅者集合。看名字是做缓存订阅者用
private static final Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<>();
//key为事件类型Class,value为订阅者集合,一个事件可以有多个订阅者
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
//key为订阅者对象,value为这个订阅者订阅的事件Class集合
private final Map<Object, List<Class<?>>> typesBySubscriber;
//粘性事件 key:粘性事件的class对象, value:事件对象
private final Map<Class<?>, Object> stickyEvents;
//用于android构造主线程Poster的接口
private final MainThreadSupport mainThreadSupport;
//事件转发到主线程处理
private final Poster mainThreadPoster;
//事件转发到后台线程处理
private final BackgroundPoster backgroundPoster;
//事件转发到异步线程处理
private final AsyncPoster asyncPoster;
//用于查找订阅方法
private final SubscriberMethodFinder subscriberMethodFinder;
//线程切换到非UI线程执行订阅的方法体
private final ExecutorService executorService;
//是否支持事件继承
private final boolean eventInheritance;
//各种开关
private final boolean throwSubscriberException;
private final boolean logSubscriberExceptions;
private final boolean logNoSubscriberMessages;
private final boolean sendSubscriberExceptionEvent;
private final boolean sendNoSubscriberEvent;
可以看出核心的属性主要包括描述事件与订阅者的多对多映射集合,三中用于线程切换的转发器以及查询订阅方法的SubscriberMethodFinder。
再看建造方法方法:
public EventBus() {
this(DEFAULT_BUILDER);
}
//通过Builder模式构造
EventBus(EventBusBuilder builder) {
//日志
logger = builder.getLogger();
//事件类型与订阅者的多对多映射集合
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
//粘性事件集合
stickyEvents = new ConcurrentHashMap<>();
mainThreadSupport = builder.getMainThreadSupport();
//三个Poster
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
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;
//任务切换到非UI线程的任务执行线程池
executorService = builder.executorService;
}
注册源码分析
注册流程
/**
* Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
* are no longer interested in receiving events.
* <p/>
* Subscribers have event handling methods that must be annotated by {@link Subscribe}.
* The {@link Subscribe} annotation also allows configuration like {@link
* ThreadMode} and priority.
*/
public void register(Object subscriber) {
//1.订阅方法的"宿主"订阅对象Class
Class<?> subscriberClass = subscriber.getClass();
//2.查找订阅对象上的订阅方法集合
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
//3.遍历订阅方法集合,根据SubscriberMethod和订阅者对象配置映射集合
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
执行流程已经在注释中写明,我们发现这个方法主要围绕SubscriberMethod来执行构造和订阅流程的,所以先看看它到底是啥:
public class SubscriberMethod {
final Method method;//订阅的方法Method
final ThreadMode threadMode;//执行在哪种线程
final Class<?> eventType;//订阅方法的入参事件类型
final int priority;//执行优先级
final boolean sticky;//是否是粘性事件
/** Used for efficient comparison */
String methodString;//类似于Method的签名,所属类名+订阅方法名+参数类型名,用于快速判等
public SubscriberMethod(Method method, Class<?> eventType, ThreadMode threadMode, int priority, boolean sticky) {
this.method = method;
this.threadMode = threadMode;
this.eventType = eventType;
this.priority = priority;
this.sticky = sticky;
}
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
} else if (other instanceof SubscriberMethod) {
checkMethodString();
SubscriberMethod otherSubscriberMethod = (SubscriberMethod)other;
otherSubscriberMethod.checkMethodString();
// Don't use method.equals because of http://code.google.com/p/android/issues/detail?id=7811#c6
return methodString.equals(otherSubscriberMethod.methodString);
} else {
return false;
}
}
private synchronized void checkMethodString() {
if (methodString == null) {
// Method.toString has more overhead, just take relevant parts of the method
StringBuilder builder = new StringBuilder(64);
builder.append(method.getDeclaringClass().getName());
builder.append('#').append(method.getName());
builder.append('(').append(eventType.getName());
methodString = builder.toString();
}
}
@Override
public int hashCode() {
return method.hashCode();
}
}
SubscriberMethod封装了订阅方法的所有信息,包括订阅方法的Method、入参事件类型、执行在哪种线程、优先级等信息。
查找订阅方法列表
回到注册方法的第二步,我们看看EventBus的核心之一:查找订阅对象上的订阅方法:SubscriberMethodFinder的findSubscriberMethods方法.
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//从缓存中取,METHOD_CACHE的key未订阅对象Class,value为SubscriberMethod列表
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
if (ignoreGeneratedIndex) {
//通过反射扫描注解得到订阅方法信息
subscriberMethods = findUsingReflection(subscriberClass);
} else {
//从注解器生成的MyEventBusIndex类中获得订阅类的订阅方法信息
subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
//加入缓存
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
继续看findUsingReflection方法:
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
//FindState用来做订阅方法的校验和保存
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findUsingReflectionInSingleClass(findState);
//向父类遍历
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
//从FindState缓存池中取,取不到再new
private FindState prepareFindState() {
synchronized (FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
FindState state = FIND_STATE_POOL[i];
if (state != null) {
FIND_STATE_POOL[i] = null;
return state;
}
}
}
return new FindState();
}
最终会调用findUsingReflectionInSingleClass方法:
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;
}
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注解
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)) {
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");
}
}
}
这里走完,findState里面保存了一个订阅者Class上的所有订阅方法信息,再回到之前的findUsingReflection方法:
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findUsingReflectionInSingleClass(findState);
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
返回时调用getMethodsAndRelease,经过循环调用后,遍历完subscriberClass和它的父类之后,所有订阅方法信息都保存在findState中,这个方法很显然是从findState取出SubscriberMethod列表返回:
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
//从findState中取出SubscriberMethod列表
List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
//findState复用
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;
}
添加订阅方法校验
在上面的查阅方法中涉及到一个很重要的类FindState,它负责校验一个Method是否可以被注册到总线中去,下面看看它如何校验:
//从执时间类型到方法的映射,用作第一层校验
final Map<Class, Object> anyMethodByEventType = new HashMap<>();
//从执方法签名到订阅类映射,用作第二层校验,负责根据methodKey覆盖父类订阅方法
final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
boolean checkAdd(Method method, Class<?> eventType) {
//两层校验
// 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.
// Usually a subscriber doesn't have methods listening to the same event type.
Object existing = anyMethodByEventType.put(eventType, method);
if (existing == null) {
//代码1:事件类型对应的Method第一次加入,表示可以注册到总线
return true;
} else {
//事件类型对应的Method已经存在,则做第二层校验
if (existing instanceof Method) {
//代码2:校验之前的方法,如果不通过则抛异常,这里我不是很理解,校验失败为啥要抛异常,返回false不添加它不可以吗?
if (!checkAddWithMethodSignature((Method) existing, eventType)) {
// Paranoia check
throw new IllegalStateException();
}
// Put any non-Method object to "consume" the existing Method
//代码3:代码执行到这里,表示之前的Method(existing)校验通过,这里替换原来的value为FindState对象,这个事件类型对应的订阅方法不止一个,之前的Method类型的value被覆盖为FindState类型,表示被覆盖的就Method对象通过第二层校验,如果继续有新Method进来,会直接“下放”到第二层校验,执行代码4即可
anyMethodByEventType.put(eventType, this);
}
//代码4:新进来的方法做第二层校验
return checkAddWithMethodSignature(method, eventType);
}
}
//这个方法主要是实现根据方法签名查找对应的所属类,如果待添加的方法的所属类是之前添加过Method所属类的子类(即覆写了它),则替换它,说的这么复杂,其实就是一句话:这个方法的目的就是实现子类同样的订阅方法覆盖父类的订阅方法。
//校验规则:方法签名methodKey如果如果第一次添加,则返回true,否则只允许子类的订阅方法添加
private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
//构建方法签名
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(method.getName());
methodKeyBuilder.append('>').append(eventType.getName());
String methodKey = methodKeyBuilder.toString();
//待添加方法所属的类
Class<?> methodClass = method.getDeclaringClass();
//之前方法所属的类
Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
//代码5:
//条件一:第一次添加,条件二:之前的方法所属类为当前方法所属类的父类
// Only add if not already found in a sub class
return true;
} else {
// Revert the put, old class is further down the class hierarchy
//代码6:条件都不满足,则回退,保证只有子类的方法才可添加
subscriberClassByMethodKey.put(methodKey, methodClassOld);
return false;
}
}
关于校验这块代码比较绕,我先说明一下校验的原则:
1.Method首次添加,可以注册到总线;
2.对于相同方法名和参数类型的方法,新来的Method如果是子类(或者相同)的订阅方法,则覆盖,可以注册,反之不行;
3.一个订阅者对象可以持有同一个EnventType参数的多个订阅方法,即一个订阅者可以持有多个接收相同事件的方法。
代码注释我尽量解释详细,比较绕。这里我举一个例子来解释校验流程:
假设订阅者持有三个订阅方法,且参数类型相同:M1(type)、M2(type)、M3(type),执行流程
1.M1首次添加,走代码1,返回true;
2.M2添加到anyMethodByEventType,返回M1,走代码2,M1做二层校验通过,anyMethodByEventType中type对应的value为FindState对象state,然后走代码4,M2做二层校验;
3.M3添加到anyMethodByEventType,返回上一步的对象state,走代码4,二层校验M3;
5.M1、M2、M3的方法名不同,所以methodKey也不同,所以都可以通过checkAddWithMethodSignature的二层校验。
PS:上面我们只考虑一个类中的同事件类型的订阅方法,大家考虑下面一种情况:
类B集成类A,A有订阅方法A1(type),A2(type),并且在B类覆写了这俩方法B1(type),B2(type),且加上了订阅注解,大家自己分析完流程,或者写Demo调试一下就可以发现问题,这里是问题描述,希望哪位大神解答!
订阅方法注册到总线
到目前为止我们拿到了一个订阅者上所有的订阅者方法,现在我们需要将它注册到总线当中,回到注册方法:
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
//查找订阅者方法
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
//注册订阅者方法
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
subscribe方法实现注册:
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//拿到方法的事件类型
Class<?> eventType = subscriberMethod.eventType;
//构造订阅者,其实就是订阅方法持有者subscriber和subscriberMethod的封装,和subscriberMethod一一对应
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//subscriptionsByEventType的key为事件类型,value为Subscription列表,从事件类型到Subscription一对多映射
//取Subscription集合
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
//集合为空则新建,put
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);
}
}
//按订阅方法优先级插入Subscription列表
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;
}
}
//typesBySubscriber的key为订阅者subscriber,value为事件类型集合,从订阅者到事件类型的一对多映射,用于注销
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//如果是粘性事件,注册往往滞后于发送事件,要立即提交执行
if (subscriberMethod.sticky) {
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.根据订阅对象subscriber和订阅方法subscriberMethod构造订阅者(Subscription),注意订阅方法和订阅者是一一对应的关系,订阅方法的调用者就是订阅对象,一个订阅对象可以持有多个订阅者;
2.订阅对象按优先级添加到subscriptionsByEventType集合;
3.一个订阅对象只能被注册一次,否则抛出异常;
4.事件类型添加到typesBySubscriber集合,typesBySubscriber是订阅者到事件类型的映射集合,用于注销.
5.如果是粘性事件,由于它的注册往往滞后于发送操作,所以在注册的时候立即提交执行。
事件分发
分发流程
前面经过山路十八弯,分析了完阅方法的收集和注册,终于到了最后步核心功能-事件的提交了,看post方法:
/** Posts the given event to the event bus. */
public void post(Object event) {
//1.先拿到PostingThreadState对象
PostingThreadState postingState = currentPostingThreadState.get();
//2.取事件队列
List<Object> eventQueue = postingState.eventQueue;
//3.添加事件
eventQueue.add(event);
if (!postingState.isPosting) {
//注意:这里postingState.isMainThread标记的post方法执行所在的线程是否在主线程,用于线程切换。
postingState.isMainThread = isMainThread();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
while (!eventQueue.isEmpty()) {
//4循环分发事件
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
首先是通过currentPostingThreadState.get()方法来得到当前线程PostingThreadState的对象,为什么是说当前线程我们来看看currentPostingThreadState的实现:
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
发现currentPostingThreadState是一个ThreadLocal,也就是所线程私有数据,再继续看看它保存了哪些东西:
/** For ThreadLocal, much faster to set (and get multiple values). */
final static class PostingThreadState {
final List<Object> eventQueue = new ArrayList<>();//当前总线上的事件队列
boolean isPosting;//是否正在分发事件
boolean isMainThread;//post方法是否执行在主线程
Subscription subscription;//正在分发的订阅者,用于取消事件分发
Object event;//正在分发的事件对象,用于取消事件分发
boolean canceled;//是否取消
}
我们发现它保存了总线上的事件队列和一些状态信息。
再回到post方法,最后会循环执行postSingleEvent分发单个事件:
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
//拿到事件类型
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {//支持事件类型继承,它表示一个子类事件能否响应父类事件的订阅方法。
//获取到eventClass所有父类的集合,包括eventClass本身
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
//遍历eventTypes集合,分发event事件
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
//分发事件
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
//分发event事件
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
if (!subscriptionFound) {
if (logNoSubscriberMessages) {
logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
主要是判断是否支持子类事件触发父类事件的订阅方法,如果支持则遍历它的父类集合(包括本事件类)执行单个事件的分发方法;最后都会执行到postSingleEventForEventType方法:
//根据事件类型分发单个事件
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
//订阅者集合
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
//取eventClass对应的订阅者集合
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
//遍历订阅者集合
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
//分发事件到三种poster,这也是切换线程执行订阅方法的地方
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;
}
最后会调用postToSubscription方法做线程切换执行订阅方法,看这个方法之前我们先来了解下订阅方法中的ThreadMode:
/*
* Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.greenrobot.eventbus;
/**
* Each subscriber method has a thread mode, which determines in which thread the method is to be called by EventBus.
* EventBus takes care of threading independently from the posting thread.
*
* @see EventBus#register(Object)
* @author Markus
*/
public enum ThreadMode {
/**
* Subscriber will be called directly in the same thread, which is posting the event. This is the default. Event delivery
* implies the least overhead because it avoids thread switching completely. Thus this is the recommended mode for
* simple tasks that are known to complete in a very short time without requiring the main thread. Event handlers
* using this mode must return quickly to avoid blocking the posting thread, which may be the main thread.
*/
POSTING,//订阅方法和post方法执行在同一个线程,也就是说不进行线程切换
/**
* On Android, subscriber will be called in Android's main thread (UI thread). If the posting thread is
* the main thread, subscriber methods will be called directly, blocking the posting thread. Otherwise the event
* is queued for delivery (non-blocking). Subscribers using this mode must return quickly to avoid blocking the main thread.
* If not on Android, behaves the same as {@link #POSTING}.
*/
MAIN,//订阅方法执行在UI线程,如果post执行在UI线程,则立即执行,此时会阻塞post方法,如果post执行在子线程,则订阅方法会加入消息队列,交给MainLooper的Handler处理.
/**
* On Android, subscriber will be called in Android's main thread (UI thread). Different from {@link #MAIN},
* the event will always be queued for delivery. This ensures that the post call is non-blocking.
*/
MAIN_ORDERED,//运行在主线程,和MAIN,不同的是,不管post是否运行在主线程,订阅方法都会加入消息队列,交给MainLooper的Handler处理,而不会阻塞post方法。
/**
* On Android, subscriber will be called in a background thread. If posting thread is not the main thread, subscriber methods
* will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single
* background thread, that will deliver all its events sequentially. Subscribers using this mode should try to
* return quickly to avoid blocking the background thread. If not on Android, always uses a background thread.
*/
BACKGROUND,//运行在后台线程,如果post运行在子线程,订阅方法直接执行,post运行在主线程,订阅方法运行在一个全局后台线程,这个订阅方法加入后台运行队列中,会阻塞其他后台运行的方法,所以需要尽快返回
/**
* Subscriber will be called in a separate thread. This is always independent from the posting thread and the
* main thread. Posting events never wait for subscriber methods using this mode. Subscriber methods should
* use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number
* of long running asynchronous subscriber methods at the same time to limit the number of concurrent threads. EventBus
* uses a thread pool to efficiently reuse threads from completed asynchronous subscriber notifications.
*/
ASYNC //订阅方法执行在单独的线程,异步执行,独立于post线程和主线程,为了避免造成大量的异步线程创建和销毁,采用线程池维护异步线程。
}
//结合注释,ThreadMode的几个类型意义如下:
1.POST:默认的 ThreadMode,表示在执行 Post 操作的线程直接调用订阅者的事件响应方法;
2.Main:在主线程中执行响应方法,订阅方法不能执行耗时操作,会阻塞运行在主线程的post方法;
- MAIN_ORDERED:运行在主线程,和MAIN,不同的是,不管post是否运行在主线程,订阅方法都会加入消息队列,交给MainLooper的Handler处理,而不会阻塞post方法;
- BACKGROUND:运行在后台线程,如果post运行在子线程,订阅方法直接执行,post运行在主线程,订阅方法运行在一个全局后台线程,这个订阅方法加入后台运行队列中,会阻塞其他后台运行的方法,所以需要尽快返回;
- ASYNC:订阅方法执行在单独的线程,异步执行,独立于post线程和主线程,为了避免造成大量的异步线程创建和销毁,采用线程池维护异步线程.
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
//取订阅方法的执行线程类型
switch (subscription.subscriberMethod.threadMode) {
case POSTING://不进行线程切换,和post方法执行在同一线程
invokeSubscriber(subscription, event);
break;
case MAIN://运行在主线程
if (isMainThread) {//post方法也运行在UI线程,则直接调用
invokeSubscriber(subscription, event);
} else {//post运行在子线程,则提交给HandlerPoster
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) {
//如果post运行在主线程,则将订阅方法提交到后台任务队列
backgroundPoster.enqueue(subscription, event);
} else {
//如果post执行在子线程,则立即执行订阅者方法。
invokeSubscriber(subscription, event);
}
break;
case ASYNC:
//交给异步线程执行
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
- POSTING:不论该线程是否为主线程(UI 线程)。当该线程为主线程时,响应方法中不能有耗时操作,否则有卡主线程的风险。适用场景:对于是否在主线程执行无要求,但若 Post 线程为主线程,不能耗时的操作;
2.Main:如果发布线程就是主线程,则直接调用订阅者的事件响应方法,否则通过主线程的 Handler 发送消息在主线程中处理——调用订阅者的事件响应函数。显然,MainThread类的方法也不能有耗时操作,以避免卡主线程。适用场景:必须在主线程执行的操作; - MAIN_ORDERED:无论post是否执行在UI线程,订阅方法都会提交到主线程消息队列,不会阻塞post方法;
- BACKGROUND:在后台线程中执行响应方法。如果发布线程不是主线程,则直接调用订阅者的事件响应函数,否则启动唯一的后台线程去处理。由于后台线程是唯一的,当事件超过一个的时候,它们会被放在队列中依次执行,因此该类响应方法虽然没有PostThread类和MainThread类方法对性能敏感,但最好不要有重度耗时的操作或太频繁的轻度耗时操作,以造成其他操作等待。适用场景:操作轻微耗时且不会过于频繁,即一般的耗时操作都可以放在这里;
5.Async:不论发布线程是否为主线程,都使用从线程池取一个空闲线程来处理,和Background不同的是,Async类的所有线程是相互独立的,因此不会出现卡线程的问题。适用场景:长耗时操作,例如网络访问。
总结
EventBus的整体思想是使用了观察者模式将发布者和接收者实现解耦,内部有以下角色:
1.订阅者: Subscription用于订阅方法的调用,内部的subscriber为方法宿主,SubscriberMethod为订阅方法;
2.事件:总线上传递的对象,也是post方法和订阅方法的入参;
3.事件发布者就是EnventBus对象;
整体设计思想描述:
1.根据反射和注解将所有订阅者方法注册到总线,EventBus就持有了某一个事件类型有哪些订阅者,以便事件分发;
2.客户端通过EventBus发布事件event;
3.EventBus内部查找订阅event事件类型的所有订阅者集合;
4.遍历订阅者集合,并按照订阅方法的线程模式分发到Poster上执行订阅方法.
整体的设计类图如下:
类图摘自[CodeKK的EventBus源代码分析
]
本篇走完了事件注册和分发的流程,限于篇幅,另外一部分重要内容分发器(Poster)和注销方法放在下一篇讲。