EventBus是GreenRobot使用发布/订阅模式进行松耦合的框架。
官网中对EventBus的事件传递图如下:
EventBus的简单使用可以根据官网入门教程进行学习。
EventBus3基本使用
添加依赖:
implementation 'org.greenrobot:eventbus:3.1.1'
1.定义事件MsgEvent,事件实际是POJO。
package future.cn.demo;
public class MsgEvent {
public final String message;
public MsgEvent(String message) {
this.message = message;
}
}
2.定义订阅方法(即事件的回调方法),并注册订阅者。
package future.cn.demo;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EventBus.getDefault().register(this);
Button button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, SecondActivity.class));
}
});
}
@Override
protected void onStart() {
super.onStart();
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(MsgEvent event){
Toast.makeText(this, event.message, Toast.LENGTH_SHORT).show();
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}
定义订阅回调方法,方法名称在EventBus3中是可以随意定义的。在定义的方法添加@Subscribe注解。
注册订阅者则通过
EventBus.getDefault().register(this);
在跳转的SecondActivity中onDestroy方法发送事件,即在关闭SecondActivity时进行事件传送。
package future.cn.demo;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import org.greenrobot.eventbus.EventBus;
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().post(new MsgEvent("hello world"));
}
}
返回到MainActivity的时候可以接收到发送过来的事件。
处理线程
EventBus发送事件的线程和处理事件的线程可以不同。EventBus中通过ThreadMode进行定义在哪个线程调用订阅方法。
ThreadMode是一个枚举类型,定义了五个枚举实例。现在分别对其进行说明。
1.POSTING
这也是默认的线程模式,事件传送是同步的进行的,而且一经post所有的订阅者会立即回调。这种模式也表明不需要切换线程,只适用于简单任务,短时即可完成。 如果post线程是主线程的话,不应该做耗时任务,防止阻塞线程。
2.MAIN
订阅者会在主线程被调用。如果Post 线程是主线程的话,事件处理方法则会直接被调用。事件处理方法不能做耗时任务,防止阻塞主线程。
3.MAIN_ORDRERED
这种模式,订阅者会在Android主线程被调用(不能处理耗时任务),事件会被加入队列用于后面传送到订阅者。如果有订阅者线程模式为MAIN和MAIN_OREDERED,则post事件之后,总是MAIN模式的订阅者事件处理方法先被调用,因为MAIN_ORDERED模式的订阅方法会被加入队列
4.BACKGROUND
订阅者会在后台线程被调用,如果posting线程不是主线程,则事件处理方法会在posting线程直接被调用,如果posting线程是主线程,则EventBus会使用一个单独的后台线程按顺序发送所有事件,事件处理应该很快结束防止阻塞后台线程。
5.ASYNC
事件处理方法在不同线程被调用。通常独立于posting线程以及主线程。这种模式下发送事件不会等待事件处理方法。通常此模式用于事件处理方法需要做耗时操作。EventBus使用线程池进行线程复用。
粘性事件
EventBus支持粘性事件,即在发布之后订阅也可以收到的事件。EventBus将最后的粘性事件保存在内存中。
发送粘性事件:
EventBus.getDefault().postSticky(new MsgEvent("hello activity"));
在register之前post粘性事件,在register过程中会所有sticky为true的订阅者都会收到粘性事件。
此外,还可以通过显式获取粘性事件
EventBus.getDefault().getStickyEvent(MsgEvent.class);
在处理完粘性事件后需要将其移除,以防后续订阅者会收到此粘性事件。
EventBus.getDefault().removeStickyEvent(MsgEvent.class);
源码解析
1.首先看register方法:
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
register方法中首先获取订阅者的Class对象,在第3行中获取订阅者方法列表,通过方法subscriberMethodFinder.findSubscriberMethods(subscriberClass);
进入findSubscriberMethods,首先通过METHOD_CACHE获取subscriberMethods,初次进去应该为空。
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
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;
}
}
第7行判断ignoreGeneratedIndex,默认为false,在创建EventBus实例的时候通过EventBusBuilder设置。此处为false调用findUsingInfo。
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
FindState findState = prepareFindState();//创建FindState实例,或者从FindState池中获取一个实例
findState.initForSubscriber(subscriberClass);//初始化FindState
while (findState.clazz != null) {//clazz在初始化时赋值为subscriberClass,不为空。
findState.subscriberInfo = getSubscriberInfo(findState);//获取subscriberInfo
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);
}
在第5行时,通过getSubdcriberInfo获取subscriberInfo
private SubscriberInfo getSubscriberInfo(FindState findState) {
if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
if (findState.clazz == superclassInfo.getSubscriberClass()) {
return superclassInfo;
}
}
if (subscriberInfoIndexes != null) {
for (SubscriberInfoIndex index : subscriberInfoIndexes) {
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
if (info != null) {
return info;
}
}
}
return null;
}
很显然在第二行中findState.subscriberInfo为空,因此会走到8行,判断subscriberInfoIndexes,subscriberInfoIndexes在EventBusBuilder中进行的初始化
public EventBusBuilder addIndex(SubscriberInfoIndex index) {
if (subscriberInfoIndexes == null) {
subscriberInfoIndexes = new ArrayList<>();
}
subscriberInfoIndexes.add(index);
return this;
}
因此这个subscriberInfoIndexes为空(这个地方后面再讲,与索引加速有关),因此会走findUsingReflectionInSingleClass方法,从方法名可以看出是通过反射进行查找。进入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 subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
Class<?> eventType = parameterTypes[0];
if (findState.checkAdd(method, eventType)) {
ThreadMode threadMode = subscribeAnnotation.threadMode();
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");
}
}
}
SubscriberMethod是对订阅者回调方法的封装,在for循环中通过反射获取订阅者方法,最后将SubscriberMethod添加到subscriberMethods容器中。再回到register方法中,将获取到的subscriberMethods通过循环遍历调用subscribe方法。
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);//获取subscriberMethods
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;//自定义MsgEvent的Class对象
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);//Subscription是对subscriber和subscriberMethod的封装
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);//通过Map容器将事件类型与对应的订阅者和订阅方法对应起来,以便与后面post的时候进行查找
} 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;
}
}//根据优先级将subscriberMethod放入
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
if (subscriberMethod.sticky) {//在注册过程中,对已经post的粘性事件发送给订阅者。
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);
}
}
}
从上面代码可以看出,除了将订阅者信息进行保存,还处理了粘性事件,粘性事件被暂时保存在Map中,具体后面分析。2.接下来看post方法
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
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;
}
}
}
currentPostingThreadState是一个ThreadLocal变量,初始值通过initialValue创建。
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
post方法最后调用postSingleEvent()方法。
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) {
logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
第4行中eventInheritance默认为true。因此会去调用lookupAllEventTypes。进入lookupAllEventTypes
private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
synchronized (eventTypesCache) {
List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
if (eventTypes == null) {
eventTypes = new ArrayList<>();
Class<?> clazz = eventClass;
while (clazz != null) {
eventTypes.add(clazz);
addInterfaces(eventTypes, clazz.getInterfaces());
clazz = clazz.getSuperclass();
}
eventTypesCache.put(eventClass, eventTypes);
}
return eventTypes;
}
}
通过循环查找继承树,将所有事件类型(比如自定义的MsgEvent类)全部找到,包括事件类型的父类,一直到Object类。获取到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;
}
获取了注册的订阅者信息之后调用postToSubscription方法。进入postToSubscription方法
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
invokeSubscriber(subscription, event);//通过方法名可以看出是调用订阅者方法,跟post方法在同一个线程。
break;
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);//如果当前是主线程,则直接调用
} else {
mainThreadPoster.enqueue(subscription, event);//如果post不在主线程,则先添加到队列
}
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);
}
}
下面逐个分析,posting就不说了,直接在posting线程进行调用。
(1).MAIN模式下,如果posting线程是主线程,则直接调用。如果不是主线程则通过
mainThreadPoster.enqueue(subscription, event);
进入enqueue方法,此方法实现在HandlerPost,HandlerPost是一个Handler,此类中内部维护者一个队列。
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!handlerActive) {
handlerActive = true;
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
}
}
}
先将事件加入队列,然后通过发送消息到HandlerPost所在线程进行处理即主线程。
@Override
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
while (true) {
PendingPost pendingPost = queue.poll();
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
handlerActive = false;
return;
}
}
}
eventBus.invokeSubscriber(pendingPost);//调用订阅方法
long timeInMethod = SystemClock.uptimeMillis() - started;
if (timeInMethod >= maxMillisInsideHandleMessage) {
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
rescheduled = true;
return;
}
}
} finally {
handlerActive = rescheduled;
}
}
(2).BACKGROUND模式下,如果post不在主线程,则直接调用订阅者方法。如果在主线程,则通过executorService开启一个新线程,等待一秒之后在进行调用订阅者方法。具体代码见BackgroundPoster。
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!executorRunning) {
executorRunning = true;
eventBus.getExecutorService().execute(this);
}
}
}
@Override
public void run() {
try {
try {
while (true) {
PendingPost pendingPost = queue.poll(1000);
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
executorRunning = false;
return;
}
}
}
eventBus.invokeSubscriber(pendingPost);
}
} catch (InterruptedException e) {
eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
}
(3).ASYNC模式下,直接开启另外的线程进行调用订阅者方法。
3.post粘性事件
粘性事件会保存在Map中,同时通过post方法将此事件传递出去。粘性事件可以通过显式获取,如果粘性事件在register之前post,在register过程中,会将粘性事件post出去。这里有两种情况:1.如果粘性事件在register之前post,那么如果订阅方法属性sticky为false,则是不能收到粘性事件的。2.如果在register之后调用postSticky,不管是否sticky=true,都可以收到。根据源码可以看出,postSticky方法里面调用了post方法。
4.索引加速
最后一部分是索引加速,前面在讲解获取订阅者方法的时候是通过反射获取的,运行时通过反射获取效率是很低的。因此EventBus3 增加了一部分索引加速。这里要用到编译时注解。
在build.gradle中需要添加如下配置
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
arguments = [ eventBusIndex : 'cn.future.MyEventBusIndex' ]
}
}
}
}
dependencies {
compile 'org.greenrobot:eventbus:3.1.1'
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.1.1'
}
经过build之后,在build/generated/source/apt/debug/cn/future下生成文件MyEventBusIndex,代码如下:
package cn.future;
import org.greenrobot.eventbus.meta.SimpleSubscriberInfo;
import org.greenrobot.eventbus.meta.SubscriberMethodInfo;
import org.greenrobot.eventbus.meta.SubscriberInfo;
import org.greenrobot.eventbus.meta.SubscriberInfoIndex;
import org.greenrobot.eventbus.ThreadMode;
import java.util.HashMap;
import java.util.Map;
/** This class is generated by EventBus, do not edit. */
public class MyEventBusIndex implements SubscriberInfoIndex {
private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;
static {
SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();
putIndex(new SimpleSubscriberInfo(future.cn.demo.SecondActivity.class, true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onEvent", future.cn.demo.MsgEvent.class, ThreadMode.MAIN, 0, true),
}));
putIndex(new SimpleSubscriberInfo(future.cn.demo.MainActivity.class, true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onEvent2", future.cn.demo.MsgEvent.class, ThreadMode.ASYNC, 0, true),
new SubscriberMethodInfo("onEvent", future.cn.demo.MsgEvent.class, ThreadMode.MAIN),
}));
}
private static void putIndex(SubscriberInfo info) {
SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
}
@Override
public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
if (info != null) {
return info;
} else {
return null;
}
}
}
此类中,在第一次加载MyEventBusIndex的时候将SubscriberInfo保存在一个静态Map中。通过以下方法将自定义Index加进去:
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
// Now the default instance uses the given index. Use it like this:
EventBus eventBus = EventBus.getDefault();
此方法是将自定义Index加入默认的EventBus实例中。因此在有索引的情况下:
private SubscriberInfo getSubscriberInfo(FindState findState) {
if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
if (findState.clazz == superclassInfo.getSubscriberClass()) {
return superclassInfo;
}
}
if (subscriberInfoIndexes != null) {
for (SubscriberInfoIndex index : subscriberInfoIndexes) {
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
if (info != null) {
return info;
}
}
}
return null;
}
实际是通过获取静态HashMap中的SubscriberInfo,速度当然比反射要快。