相信做Android开发的很少有没听说过EventBus的,如果真没听过那只能说明你Out了。EventBus是针对Android平台优化了的发布/订阅事件总线,它的目的是简化Android组件以及线程间的通信,从而使代码更加简洁。
官方给的流程图
简单使用
- 定义事件类型
public class MessageEvent{}
- 订阅者订阅事件
eventBus.register(this)
- 发送事件
eventBus.post(messageEvent)
通过以上三步,就完成一次完整的EventBus使用流程。
其实类似这种发布/订阅的机制在生活中用的也挺多的。比如我晚上定个8点的闹钟,就相当于订阅了一个事件,这个事件就是'8点钟的闹钟'。当第二天8点闹钟响的时候,就相当于发送了事件。我就会收到这个事件,然后执行相应的动作,比如起床。
那么,既然是订阅/发布。肯定就会有订阅,有发布了。这篇文章先根据源码来研究下订阅事件的流程和原理。
一般我们都会像这样EventBus.getDefault().register(this)
使用EventBus。这里首先会创建一个默认的EventBus对象,然后调用EventBus的register(Object subscriber)
方法将当前类作为订阅者进行事件的订阅。
/**
Convenience singleton for apps using a process-wide EventBus instance.
*/
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
可以看到,源码是通过单例模式来维护一个EventBus的实例的。从注释中看以看到,这个实例的作用于为整个进程。
这里的关键点就是register(Object subscriber)
方法了,上源码:
public void register(Object subscriber) {
register(subscriber, false, 0);
}
private synchronized void register(Object subscriber, boolean sticky, int priority) {
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
for (SubscriberMethod subscriberMethod : subscriberMethods) {
//遍历所有订阅方法进行订阅
subscribe(subscriber, subscriberMethod, sticky, priority);
}
}
可以看到register(Object subscriber)
方法内部调用了重载的方法register(Object subscriber, boolean sticky, int priority)
,所有的逻辑都在这个重载的方法内部实现。
首先执行subscriberMethodFinder.findSubscriberMethods(subscriber.getClass())
,我们跟踪到findSubscriberMethods(Class<?> subscriberClass)
方法内部去看看。
//寻找subscriberClass中所有的订阅方法
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
String key = subscriberClass.getName();
List<SubscriberMethod> subscriberMethods;
synchronized (methodCache) {//先从缓存中根据类名找对应的方法,如果没找到说明这个类之前没有注册过
subscriberMethods = methodCache.get(key);
}
if (subscriberMethods != null) {//如果找到了则直接返回,不用再查找了
return subscriberMethods;
}
subscriberMethods = new ArrayList<SubscriberMethod>();
Class<?> clazz = subscriberClass;
HashMap<String, Class> eventTypesFound = new HashMap<String, Class>();
StringBuilder methodKeyBuilder = new StringBuilder();
while (clazz != null) {
String name = clazz.getName();
if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {//排除系统的一些类
// Skip system classes, this just degrades performance
break;
}
// Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
try {
// This is faster than getMethods, especially when subscribers a fat classes like Activities
Method[] methods = clazz.getDeclaredMethods();//拿到订阅者中声明的所有方法
filterSubscriberMethods(subscriberMethods, eventTypesFound, methodKeyBuilder, methods);//对方法进行过滤,找到符合订阅规则的订阅方法
} catch (Throwable th) {
th.printStackTrace();
// Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
Method[] methods = subscriberClass.getMethods();
subscriberMethods.clear();
eventTypesFound.clear();
filterSubscriberMethods(subscriberMethods, eventTypesFound, methodKeyBuilder, methods);
break;
}
clazz = clazz.getSuperclass();
}
if (subscriberMethods.isEmpty()) {//查找完后如果没找到,报异常:该类订阅了EventBus,但没有对应的订阅方法
throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called "
+ ON_EVENT_METHOD_NAME);
} else {
synchronized (methodCache) {//如果找到了,则存起来。防止重复查找。这里的key就是方法名
methodCache.put(key, subscriberMethods);
}
return subscriberMethods;
}
}
//对方法进行过滤,筛选出符合事件接收的方法 形如 "public void onEvent**(Object event)"
private void filterSubscriberMethods(List<SubscriberMethod> subscriberMethods,
HashMap<String, Class> eventTypesFound, StringBuilder methodKeyBuilder,
Method[] methods) {
for (Method method : methods) {
String methodName = method.getName();
if (methodName.startsWith(ON_EVENT_METHOD_NAME)) {//方法以onEvent开头
int modifiers = method.getModifiers(); //方法的限定符类型 public private等
Class<?> methodClass = method.getDeclaringClass();//声明方法的类
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
//方法是public的 且非abstract static (bridge synthetic 这两个啥意思?)
Class<?>[] parameterTypes = method.getParameterTypes(); //方法参数类型
if (parameterTypes.length == 1) {//方法只有一个参数
ThreadMode threadMode = getThreadMode(methodClass, method, methodName);
if (threadMode == null) {//不是订阅方法,继续迭代下一个方法
continue;
}
Class<?> eventType = parameterTypes[0];
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(methodName);
methodKeyBuilder.append('>').append(eventType.getName());
String methodKey = methodKeyBuilder.toString();
Class methodClassOld = eventTypesFound.put(methodKey, methodClass);//以方法名和event类型名作为key
//存入订阅方法列表中
if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
// Only add if not already found in a sub class
subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));
} else {
// Revert the put, old class is further down the class hierarchy
eventTypesFound.put(methodKey, methodClassOld);
}
}
} else if (!skipMethodVerificationForClasses.containsKey(methodClass)) {
Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + methodClass + "."
+ methodName);
}
}
}
}
//判断订阅方法需要在哪个线程中调用
private ThreadMode getThreadMode(Class<?> clazz, Method method, String methodName) {
String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length());//截取方法名中onEvent后的字符
//如 onEventMainThread -> MainThread
ThreadMode threadMode;
if (modifierString.length() == 0) { //方法名为onEVent 默认在PostThread中调用
threadMode = ThreadMode.PostThread;
} else if (modifierString.equals("MainThread")) {//方法名为onEVentMainThread 在MainThread中调用
threadMode = ThreadMode.MainThread;
} else if (modifierString.equals("BackgroundThread")) {//方法名为onEVentBackgroundThread 在BackgroundThread中调用
threadMode = ThreadMode.BackgroundThread;
} else if (modifierString.equals("Async")) {//方法名为onEVentAsync 异步调用
threadMode = ThreadMode.Async;
} else {//如果不是以上几个选择 认为不是订阅方法 threadMode为null
if (!skipMethodVerificationForClasses.containsKey(clazz)) {
throw new EventBusException("Illegal onEvent method, check for typos: " + method);
} else {
threadMode = null;
}
}
return threadMode;
}
上面代码比较长,注释的已经很清楚了。findSubscriberMethods(Class<?> subscriberClass)
执行完之后,就找到了订阅者类中所有的能够接受事件的方法并存了起来。
我们继续回到register(Object subscriber, boolean sticky, int priority)
方法中,查询完订阅方法后,对所有的方法进行迭代,然后调用subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority)
进行一一注册。
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
Class<?> eventType = subscriberMethod.eventType;
//根据事件类型取出该类事件的所有订阅者
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);
if (subscriptions == null) {//如果没有该类型的订阅者 创建一个 加入列表
subscriptions = new CopyOnWriteArrayList<Subscription>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {//如果所有订阅者中已经包含新加入的这个订阅者,提示已经订阅了
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
// Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
// subscriberMethod.method.setAccessible(true);
int size = subscriptions.size();
//遍历所有的订阅者 按照优先级(Subscription的priority属性)进行排序
for (int i = 0; i <= size; i++) {
if (i == size || newSubscription.priority > subscriptions.get(i).priority) {
subscriptions.add(i, newSubscription);
break;
}
}
//取出某个订阅者中所有的订阅事件
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<Class<?>>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
if (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);
}
}
}
当subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority)
方法执行完之后,所有的订阅者以及订阅者中的事件接受方法就被存起来了。
//按事件类型存放的订阅者集合
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
//按订阅者类型存放的事件集合
private final Map<Object, List<Class<?>>> typesBySubscriber;
//黏性事件(这个怎么理解?)
private final Map<Class<?>, Object> stickyEvents;
至此,事件的订阅就完成了。最终的结果就是EventBus保存了订阅者中所有符合规则(即public void onEvent***(Object event)
)的能接收事件的方法。当post事件的时候,就会拿着这个事件去保存的订阅者方法中去对比寻找,找到后通过反射去调用对应的方法。