前言:已将EventBus用于了多个项目,最近研究了EventBus的源码,在此记录一下研究及学习历程。(注:本文记录的是2.4.0的版本)
一、概述
EvetnBus是一款为Android打造的订阅/发布事件总线,可以代替BroadCast、Handler等在Activity等组件间传递消息。
二、使用简介
- 首先,建一个消息类,如MsgEvent,里面可随便定义变量,也可用已有的类。
public class MsgEvent {
private String msg;
public MsgEvent(String msg) {
this.msg = msg;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
- 在需要接收消息的类里进行消息订阅(如在Activity的onCreate中进行注册,以下的消息订阅者均以Activity示例)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventBus.getDefault().register(this);
}
- 在需要接收消息的类里需定义相应的消息处理方法,该方法以“onEvent”字符串开头,且符合EventBus的四种接收方法的命名(即订阅函数,请看源码解析部分)。
public void onEventMainThread(MsgEvent event){
Log.w("----onEventMainThread----",event.getMsg()+"");
}
- 在需要发布事件的地方,进行事件发布。
EventBus.getDefault().post(new MsgEvent("msg"));
- 最后,若不想再接收消息,则取消订阅即可。
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
三、源码分析
1、订阅函数
在分析源码之前,简单介绍一下EventBus的四类订阅函数。(即:前面提到的以“onEvent”开头的消息接收方法)。
- onEvent:使用onEvent作为订阅函数,该事件在哪个线程发布出来的,onEvent就会在这个线程中运行,即发布事件和接收事件线程在同一个线程。
- onEventAsync:使用onEventAsync作为订阅函数,无论事件在哪个线程发布,都会创建新的子线程在执行此方法。
- onEventMainThread:使用onEventMainThread作为订阅函数,无论事件是在哪个线程中发布出来的,都会在UI线程中执行,接收事件就会在UI线程中运行。
- onEventBackground:使用onEventBackgrond作为订阅函数,若事件是在UI线程中发布出来的,
就会在子线程中运行;若事件本来就是子线程中发布出来的,onEventBackground函数直接在该子线程中执行。
2、核心类介绍
2.1EventBus.java
EventBus是整个事件最关键的功能类,以单例的方式提供入口给订阅者和发布者,同时提供对外的API(如订阅API,发布API,取消订阅API)。
2.1.1关键成员变量
private static final Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<Class<?>, List<Class<?>>>();
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
private final Map<Object, List<Class<?>>> typesBySubscriber;
private final Map<Class<?>, Object> stickyEvents;
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
private final HandlerPoster mainThreadPoster;
private final BackgroundPoster backgroundPoster;
private final AsyncPoster asyncPoster;
private final SubscriberMethodFinder subscriberMethodFinder;
private final ExecutorService executorService;
- eventTypesCache:该成员是一个Map集合,以事件类型为key,事件类型列表为value(列表存储:事件的所有父类和接口的Class类型)。当发布一个事件的时候,首先根据该事件的类型查找之前有没有发送过同样的事件,如果没有则进行缓存。
- subcriptionByEventType:该成员是一个Map集合,以事件类型为key,事件类型对应的订阅者列表为value。
- typesByScriscriber:该成员是一个Map集合,以订阅者为key,订阅者对应的事件类型列表为value.
- stickEvent:该成员是一个sticky事件变量,当事件发布后,若有订阅者开始订阅该类型事件,依然能收到该事件最近一个Sticky事件。
- currentPostingThreadState:该成员是ThreadLocal变量,存储了当前线程的Post信息。
- mainThreadPoster:该成员是一个Handler变量,在主线程中执行订阅方法。
- backgroundPoster:该成员是一个Runnable变量,在子线程中执行订阅方法。
- asyncPoster:该成员变量是一个Runnable变量,在子线程中执行订阅方法。
- subscriberMethodFinder:查找订阅事件相关的功能类。
- executorService:线程池变量。
2.1.2EventBus入口方法
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
getDefault方法创建EventBus的单例模式,初始化线程池等相关变量。
2.1.3事件订阅(事件注册)
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);
}
}
- 第2行:通过subscriberMethodFinder查找订阅者中对应的所有订阅函数,并以List的形式返回数据,SubcriberMethod封装了“订阅者,订阅函数,订阅函数对应的线程模式”。
- 第3行:遍历每个订阅方法,执行subscribe方法。
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
//获取订阅的事件类型(即消息类型)
Class<?> eventType = subscriberMethod.eventType;
//根据事件类型获取其对应的Subscription列表(Subscription成员变量:订阅者,subscriberMethod,优先级)
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
//创建Subscription
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);
}
}
//遍历Subscription列表
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
//比较新建的Subscription的优先级和列表中的其他项,
//只要查找到优先级高于第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) {
Object stickyEvent;
synchronized (stickyEvents) {
stickyEvent = stickyEvents.get(eventType);
}
if (stickyEvent != null) {
// If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
// --> Strange corner case, which we don't take care of here.
postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());
}
}
}
2.1.4事件注销
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
unubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
- 第2行:获取订阅者对应的事件列表
- 第4-7行:遍历事件列表,执行unubscribeByEventType方法,从subscriptionEventType中移除前订阅者;从typeBysubscriber移除当前订阅者。
2.1.4事件发布
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
if (!postingState.isPosting) {
postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
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;
}
}
}
- 第2行:获取当前线程的Post信息(PostingThreadState成员变量:事件队列,post事件是否在主线程,
- 是否正在分发,订阅者信息,是否取消)。
- 第4行:将事件添加到Post队列中。
- 第5-7行:当前线程未进行事件分发时,重置Post的信息(重置isMainThread,标示isPosting正在分发)。
- 第11-15行:循环遍历事件队列,通过posSingleEvent方法,进行事件发布。
- 第16-17行:重置当前线程的Post信息(标示isMainThread为false,标示isPosting为未分发状态)
posSingleEvent方法:
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
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 {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
if (!subscriptionFound) {
if (logNoSubscriberMessages) {
Log.d(TAG, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
- 第4行:判断是否发布有继承关系类型的事件
- 第5行:查找与事件类型有关的所有类型,包括它的父类及他实现的接口(查找方法:
- lookupAllEventTypes)
- 第7-8行:遍历类型列表,进行事件发布(发布方法:postSingleEventForEventType)。
lookupAllEventTypes方法
private List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
synchronized (eventTypesCache) {
List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
if (eventTypes == null) {
eventTypes = new ArrayList<Class<?>>();
Class<?> clazz = eventClass;
while (clazz != null) {
eventTypes.add(clazz);
addInterfaces(eventTypes, clazz.getInterfaces());
clazz = clazz.getSuperclass();
}
eventTypesCache.put(eventClass, eventTypes);
}
return eventTypes;
}
}
postSingleEventForEventType方法
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
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;
}
- 第4行:获取事件类型对应的订阅者列表。
- 第7-22行:遍历订阅这列表,通过postToSubscription方法对订阅者进行消息发布。
postToSubscription方法
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case PostThread:
invokeSubscriber(subscription, event);
break;
case MainThread:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case BackgroundThread:
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);
}
}
- 第3-5行:如果订阅者对应的订阅方法线程模式是PostThread,则在当前线程调用订阅方法。
- 第6-12行:如果订阅者对应的订阅方法线程模式是MainThread,若当前线程为主线程,则直接调用订阅方法;反之,通过mainThreadPoster的Handler机制在主线程调用订阅方法。
- 第13-19行:如果订阅者对应的订阅方法线程模式是BackgroundThread,若当前线程为主线程,则通过backgroundPoster异步执行订阅方法;反之在当前线程执行订阅方法。
- 第20-22行:如果订阅者对应的订阅方法线程模式是Async,都通过asyncPoster异步执行订阅方法。
总结:无论订阅者对应的订阅方法是哪种线程模式,都将通过invokeSubscriber方法以反射机制来执行订阅方法。
void invokeSubscriber(PendingPost pendingPost) {
Object event = pendingPost.event;
Subscription subscription = pendingPost.subscription;
PendingPost.releasePendingPost(pendingPost);
if (subscription.active) {
invokeSubscriber(subscription, event);
}
}
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);
}
}
2.2 SubscriberMethodFinder.java
提供订阅者相关信息的存储和查找功能。
2.2.1关键成员
private static final Map<String, List<SubscriberMethod>> methodCache = new HashMap<String, List<SubscriberMethod>>();
methodCache:是一个HashMap的变量,以订阅者类名为key,SubscriberMethod对象为元素的ArrayList为value。
#####2.2.2关键方法(订阅者信息的存储和查找方法)
findSubscriberMethods方法查找订阅者对应的SubscriberMethod(包括订阅者,订阅方法,订阅方法对应线程模式)列表时,先查看是否在此缓存变量中,如果存在,则直接返回列表;反之,通过反射方法查找自己及父类的订阅方法(并封装成SubscriberMethod),然后存储到methodCache,同时返回对应的SrubscriberMethod列表。
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;
HashSet<String> eventTypesFound = new HashSet<String>();
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)
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
String methodName = method.getName();
if (methodName.startsWith(ON_EVENT_METHOD_NAME)) {
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length());
ThreadMode threadMode;
if (modifierString.length() == 0) {
threadMode = ThreadMode.PostThread;
} else if (modifierString.equals("MainThread")) {
threadMode = ThreadMode.MainThread;
} else if (modifierString.equals("BackgroundThread")) {
threadMode = ThreadMode.BackgroundThread;
} else if (modifierString.equals("Async")) {
threadMode = ThreadMode.Async;
} else {
if (skipMethodVerificationForClasses.containsKey(clazz)) {
continue;
} else {
throw new EventBusException("Illegal onEvent method, check for typos: " + method);
}
}
Class<?> eventType = parameterTypes[0];
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(methodName);
methodKeyBuilder.append('>').append(eventType.getName());
String methodKey = methodKeyBuilder.toString();
if (eventTypesFound.add(methodKey)) {
// Only add if not already found in a sub class
subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));
}
}
} else if (!skipMethodVerificationForClasses.containsKey(clazz)) {
Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + clazz + "."
+ methodName);
}
}
}
clazz = clazz.getSuperclass();
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called "
+ ON_EVENT_METHOD_NAME);
} else {
synchronized (methodCache) {
methodCache.put(key, subscriberMethods);
}
return subscriberMethods;
}
}
四、注意事项
使用EventBus,混淆时需在混淆文件中配置如下:
-keep class de.greenrobot.event.**{*;}
-keepclassmembers class ** {
public void onEvent*(**);
}
五、和广播比较
广播:
- 优点:可接收系统级的事件,比如短信发送和接收,网络变化等;在BroadcastReceiver的onReceive方法中,可以获得Context,intent参数,这两个参数可以调用sdk中的方法;支持跨进程通信。
- 缺点:广播是重量级的,消耗资源较多;使用麻烦,写的代码比较多;只能在主线程接收广播。
EventBus:
- 优点:使用简单,不依赖于Context,使用时无需像广播一样关注Context的注入与传递;快速轻量;耦合度低;支持主线程/子线程的发送和接收事件。
- 缺点:不能接收系统级的事件;不能跨进程通信;混淆代码时需注意过滤OnEvent方法的混淆。(3.0支持注解:无需关注混淆配置)
总结:若需要接收系统级的事件或者跨进程通信,建议使用广播;反之,建议使用EventBus。(经过代码实践,对同一事件的接收,广播消耗的资源和消耗的事件均大于EventBus的消耗)。