引用官网的定义:
EventBus is a publish/subscribe event bus optimized for Android.
EventBus是针对Android优化的发布/订阅事件总线,主要功能是简化组件之间的消息传递。使用EventBus可以替代Handler、Intent、BroadCast在Activity、Fragment、Service和后台线程中的使用。具有开销小,代码优雅以及将发送者和接收者解耦等优点。其github网址为https://github.com/greenrobot/EventBus。
EventBus的使用
EventBus的使用可以归结为4个步骤,下面通过结合代码来进行简单说明:
1.定义事件。这里的事件就是一个普通的Java类
class AEvent{
//根据实际需求定义事件类的成员变量和方法
}
2.注册组件。以Activity为例,在onCreate()方法中注册,在onDestroy()中解注册。也可以在Fragment、Service中使用
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EventBus.getDefault().register(this);
}
public void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
3.订阅事件。在3.0之前EventBus提供了四种订阅事件的方法,onEvent,onEventMainThread,onEventBackground,onEventAsync。其区别简单来说就是区分响应事件的执行线程。在3.0后,通过 @Subscribe 注解我们可以自由的定义事件响应方法。下面是官方文档中的栗子:
// This method will be called when a MessageEvent is posted (in the UI thread for Toast)
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}
// This method will be called when a SomeOtherEvent is posted
@Subscribe
public void handleSomethingElse(SomeOtherEvent event) {
doSomethingWith(event);
}
在上面的代码中通过threadMode = ThreadMode.MAIN指定了响应事件在主线程,3.0之后通过枚举类ThreadMode指定响应事件的线程,其代码如下
public enum ThreadMode {
//默认该模式,响应事件直接在post事件的线程中执行,不适合执行耗时操作
POSTING
//在主线程(UI线程)中执行响应事件,不适合执行耗时操作,该模式对于Android来说非常有用
MAIN,
//如果是主线程分发的事件,那么响应事件会在子线程中执行。如果分发事件的是子线程,则会直接在该子线程中执行
BACKGROUND,
//无论在哪个线程分发事件,都会在新的子线程中执行。当响应事件中有耗时操作时应该选择该模式。EventBus使用线程池保证线程重用提高效率
ASYNC
}
4.发送事件。通过post方法我们可以进行事件的发送,所有注册了发送事件的方法都会得到响应。
EventBus.getDefault().post(new MessageEvent("Hello everyone!"));
以上就是EventBus的使用步骤和简要代码示例,具体的用法已经有很多资料,这里不再赘述。
之前学习EventBus也看了一些关于EventBus源码分析的文章,但是发现大部分文章发的较早,虽然原理不变但是在当前版本3.0.0中许多代码已经发生变化,因此在这里根据最新的3.0.0源码对其进行分析。
在上面代码中我们都是通过EventBus.getDefault()方法来获取实例的,其源码如下,可以看出是通过典型的单例模式实现的,通过双重加锁既保证线程安全又保证了效率。
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
我们通过register()方法来注册,在之前的文章中会告诉你有四个不同的注册方法,但在3.0.0中只有一个register方法,代码如下:
public void register(Object subscriber) {
//获取到要注册组件也就是Activity、Fragment、Service等组件的Class对象
Class> subscriberClass = subscriber.getClass();
//通过Class对象查找订阅了该组件中的Event响应方法集合
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
//遍历方法集合,调用subscribe对Event响应方法进行存储
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
从上面一段简单代码可以看出register过程主要是两步:
1. 调用findSubscriberMethods获取组件中的Event响应方法的集合;
2. 遍历集合调用subscribe()方法进行存储
接下来就看一下上面两步的实现过程
List findSubscriberMethods(Class> subscriberClass) {
//1.首先从缓存中获取方法集合,如果有直接返回
List subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//下面两个方法都是调用findUsingReflectionInSingleClass获取集合的,我们直接看findUsingReflectionInSingleClass()方法
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}
//如果集合为空说明注册的事件中没有Event响应方法会抛出异常,如果有则进行缓存然后将集合返回
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;
}
}
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();
//Event方法必须为public且不为static、abstract
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
//获取方法的参数类型数组,这里Event响应方法只能有一个参数,这很容易理解,一个方法只能对应一个事件类
Class>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
//获取@Subscribe注解对象
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
//注解不为null说明是Event响应方法
if (subscribeAnnotation != null){
//这里获取到监听的Event类的Class对象
Class> eventType = parameterTypes[0];
if (findState.checkAdd(method, eventType)) {
//获取注解标注的Event响应方法的执行线程
ThreadMode threadMode = subscribeAnnotation.threadMode();
//将订阅方法封装为SubscriberMethod对象
//SubscriberMethod对象封装了订阅方法的Method对象、执行线程、事件类型、优先级和粘性
//然后加入到FindState的subscriberMethods列表集合中,最终将该集合返回
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
}
}
}
}
通过分析源码可以明确知道是通过遍历组件中所有的方法,然后将符合订阅规则的方法封装为SubscriberMethod对象进行存储。
上面大致分析了获取Event响应方法的过程,下面来分析subscribe方法看它是怎么对响应方法进行存储的:
// Must be called in synchronized block必须在同步块中调用
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//这里获取的是Event方法所监听的Event类的Class对象
Class> eventType = subscriberMethod.eventType;
//将注册组件的Class对象和方法监听Event类的Class对象作为参数构建Subscription对象
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//根据监听的Event类型获取Subscription对象集合,如果集合为空则新建然后添加到subscriptionsByEventType集合中
CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
//如果集合不为空且包含了该Subscription对象说明已经注册过了不能重复注册,抛出异常
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
int size = subscriptions.size();
//根据索引和优先级将新的Subscription加入到列表中
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
//通过注册组件对象获取到其注册的Event类型集合,然后将该Event类型加入到集合中
List> 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).
Set, Object>> entries = stickyEvents.entrySet();
for (Map.Entry, 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);
}
}
}
上面就是整个存储过程,整个register过程就算结束了。从结果来看,就是创建了两个Map集合:以注册的组件对象作为键,Event类型作为值的根据组件存储其监听事件类型的typesBySubscriber集合和以Event的Class为键,Subscription对象为值的存储Event的注册者和响应方法的subscriptionsByEventType集合。
我们知道事件分发都是通过post方法开始的,下面我们看post的代码:
PostingThreadState封装了当前post线程的基本信息,通过ThreadLocal进行存储。ThreadLocal可以以线程作为键存储
数据,不熟悉的童鞋可以参考技术小黑屋的这篇文章:http://droidyue.com/blog/2016/03/13/learning-threadlocal-in-java/
public void post(Object event) {
//获取到事件的队列集合
PostingThreadState postingState = currentPostingThreadState.get();
List
上面的post方法,通过调用postSingleEvent()等一系列方法最终会调用到postToSubscription()根据ThreadMode信息对Event进行分发
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
//默认,直接在当前线程中执行
case POSTING:
invokeSubscriber(subscription, event);
break;
//如果是主线程,立即执行;如果不是,使用mainThreadPoster(就是一个Handler)切换到主线程执行
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
//如果是主线程中,则使用backgroundPoster切换到子线程;如果是在子线程中立即执行
case BACKGROUND:
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case ASYNC://无论是否在主线程,直接使用asyncPoster切换到一个新的子线程中执行
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
//不需要切换线程的直接调用invokeSubscriber()方法,本质上就是利用反射调用组件中的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);
}
}
上面对于不需要切换线程的方法调用很简单,我们不再赘述。接下来以MainThread为例研究下EventBus是如何切换线程的。
就主线程而言是通过mainThreadPoster对象来切换的,就是一个Handler类,下面是它的代码
final class HandlerPoster extends Handler {
private final PendingPostQueue queue;
private final int maxMillisInsideHandleMessage;
private final EventBus eventBus;
private boolean handlerActive;
HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
super(looper);
this.eventBus = eventBus;
this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
queue = new PendingPostQueue();
}
//调用mainThreadPoster的enqueue方法,首先将Subscription和Event对象封装为PendingPost对象然后存储进
//PendingPostQueue待分发事件的队列中,然后发送Message调用handleMessage作处理
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");
}
}
}
}
@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;
}
}
}
//如果不为空则调用invokeSubscriber方法通过反射调用方法
//最终还是通过invokeSubscriber(Subscription subscription, Object event)调用方法
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;
}
}
}
简而言之,就是根据ThreadMode调用相应的Handler类来进行线程的切换,然后通过反射执行Event响应方法。
上面就是整个EventBus的注册和post流程。这里画了两张图表示注册和分发过程,希望对需要的童鞋有所帮助。