一、概述

EventBus是一款针对Android优化的发布/订阅事件总线。主要功能是替代Intent,Handler,BroadCast在Fragment,Activity,Service,线程之间传递消息.优点是开销小,代码更优雅。以及将发送者和接收者解耦。
1、下载EventBus的类库
源码:https://github.com/greenrobot/EventBus

2、基本使用

(1)自定义一个类,可以是空类,比如:

public class AnyEventType {
public AnyEventType(){}
}

(2)在要接收消息的页面注册:

eventBus.register(this);  

(3)发送消息

eventBus.post(new AnyEventType event);  

(4)接受消息的页面实现(共有四个函数,各功能不同,这是其中之一,可以选择性的实现,这里先实现一个):

public void onEvent(AnyEventType event) {}  

(5)解除注册

eventBus.unregister(this);  

顺序就是这么个顺序,可真正让自己写,估计还是云里雾里的,下面举个例子来说明下。

首先,在EventBus中,获取实例的方法一般是采用EventBus.getInstance()来获取默认的EventBus实例,当然你也可以new一个又一个,个人感觉还是用默认的比较好,以防出错。

1)Android全局广播Broadcast

首先全局广播是重量级别的,并且会消耗很多资源,但是可以跨进程通信,通过以上优缺点可以知道只有一种情况下才会用到它,跨进程的时候,这个特点也是其他方案不能达到的。

2)Android本地广播Broadcast

Local Broadcast也是会消耗很多资源的,但是相比全局广播要轻量一些,他的最大优点是可以拿到Context、Intent等和Android系统紧密相关的上下文,这样就方便数据的传递和接受。

3)观察者模式

观察者观察一个主题的时候,就会把自己添加到主题的List里面,然后主题更新的时候会遍历主题List中的观察者,调用观察者模式中的update函数来更新自己,最后离开的时候要从主题中移除观察者,防止引用造成的内存泄漏。网络上面观察者模式的例子,由此可以看出观察者模式中观察者是要实现主题更新时候的接口,其实这就是和EventBus的不同,耦合性比EventBus大。

4)EventBus

EventBus中执行

    EventBus.getDefault().register(this);

的时候是让EventBus扫描当前类,把onEvent方法记录在Map里面,参数为key,方法为value,然后EventBus.getDefault().post(...)的时候,通过post里面的type来查找Map中对应的value,然后通过反射来执行我们的方法,最后别忘了在最后

EventBus.getDefault().unregister(this);

注销EventBus防止内存泄漏。
好多人说是观察者模式,但是仔细查看可以知道和观察者模式最大的不同是,主题在要更新数据的时候是通过反射来执行动作的
个人感觉EventBus的缺点是:代码阅读性降低、不能跨进程、不能混淆,但是优点很多,比观察者模式耦合性更低,比广播更轻量。

5)RxBus

原理和EventBus一样,但是如果项目中用了RxJava,就可以选择这个强于EventBus.

EventBus还有另外有个不同的函数,他们分别是:

1、onEvent
2、onEventMainThread
3、onEventBackgroundThread
4、onEventAsync

这四种订阅函数都是使用onEvent开头的,它们的功能稍有不同,在介绍不同之前先介绍两个概念:
告知观察者事件发生时通过EventBus.post函数实现,这个过程叫做事件的发布,观察者被告知事件发生叫做事件的接收,是通过下面的订阅函数实现的。

onEvent:如果使用onEvent作为订阅函数,那么该事件在哪个线程发布出来的,onEvent就会在这个线程中运行,也就是说发布事件和接收事件线程在同一个线程。使用这个方法时,在onEvent方法中不能执行耗时操作,如果执行耗时操作容易导致事件分发延迟。
onEventMainThread:如果使用onEventMainThread作为订阅函数,那么不论事件是在哪个线程中发布出来的,onEventMainThread都会在UI线程中执行,接收事件就会在UI线程中运行,这个在Android中是非常有用的,因为在Android中只能在UI线程中跟新UI,所以在onEvnetMainThread方法中是不能执行耗时操作的。
onEventBackground:如果使用onEventBackgrond作为订阅函数,那么如果事件是在UI线程中发布出来的,那么onEventBackground就会在子线程中运行,如果事件本来就是子线程中发布出来的,那么onEventBackground函数直接在该子线程中执行。
onEventAsync:使用这个函数作为订阅函数,那么无论事件在哪个线程发布,都会创建新的子线程在执行onEventAsync.

1. EventBus.getDefault();开始
入口EventBus.getDefault();开始,这里我还是很喜欢这样的静态单例对象获取方法名,我本人也很喜欢使用单例的时候,取名getDefault()。

/** 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;
}
很简单,就是一个单例设计代码,看看构造方法。

    public EventBus() {
this(DEFAULT_BUILDER);
} EventBus(EventBusBuilder builder) {
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), );
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : ;
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;
executorService = builder.executorService;
}
构造方法中创建了几个容器,用来装各种订阅者信息的,很关键的几个成员我们需要注意,mainThreadPoster 、backgroundPoster 、asyncPoster ,这个几个是后来post消息的时候,用的到的,就是利用他们将消息在不同的线程发送出去,达到在不同线程中执行的效果。
mainThreadPoster:很显然是一个自定义扩展后的Handler,在构造的时候传入的是主线的Looper,所以它是一个主线程的对应的Handler。
这里之间先看看这个Handler的代码吧:

 * Copyright (C) - Markus Junginger, greenrobot (http://greenrobot.org)
package org.greenrobot.eventbus; import android.os.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();
} 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;
}
}
}
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;
}
}
}
没错,就是传入一个MainThread的Looper,创建了一个主线程的Handler,毋庸置疑,通过他发送的事件消息,都是在主线中执行的。
backgroundPoster : 那这个是什么呢,我们来看看代码。

 * Copyright (C) - Markus Junginger, greenrobot (http://greenrobot.org)
package org.greenrobot.eventbus; import android.util.Log; /**
* Posts events in background.
*
* @author Markus
*/
final class BackgroundPoster implements Runnable { private final PendingPostQueue queue;
private final EventBus eventBus; private volatile boolean executorRunning; BackgroundPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
} 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();
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) {
Log.w("Event", Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
}
}
很明显他是一个实现了Runnable 的对象,所以它的载体肯定是一个子线程,是给某子线程执行具体任务的。
 
asyncPoster :再来看看这个,这个听名字就感觉已经知晓大部分信息了,异步Poster,可定也是在单独线程中post任务的。

class AsyncPoster implements Runnable {

    private final PendingPostQueue queue;
private final EventBus eventBus; AsyncPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
} public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
queue.enqueue(pendingPost);
eventBus.getExecutorService().execute(this);
} @Override
public void run() {
PendingPost pendingPost = queue.poll();
if(pendingPost == null) {
throw new IllegalStateException("No pending post available");
}
eventBus.invokeSubscriber(pendingPost);
}
}
所以这里就不用多说了,也是一个实现了Runnable 的对象,一定也是被子线程执行的具体任务。
 
所以最后我们都能看到上面三段代码两个共同的两行代码:
eventBus.invokeSubscriber(pendingPost);  这行代码其实就是在使用反射调用订阅的者的事件方法。
 eventBus.getExecutorService().execute(this); 这行代码很明显就是把时间提交给一个空闲的线程去执行。
当然HandlerPoster 就除外了,当然他是使用sendMessage的方式去去执行一个具体的事件,其实对应的也是 eventBus.getExecutorService().execute(this);这行代码。
 
OK,这里我们就浅层次的分析到这里,我们回到主线,继续。
 
 
2.register(Object subcriber)注册订阅者
 
我们通过EventBus的getDefualt()方法获取了一个EventBus的对象,然后就可以通过他来注册地订阅者了,接下来就看看EventBus的register(Object subcriber)方法是如果实现的呢?

    public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
OK,从上面的代码中,我们可以看到调用了subscriberMethodFinder对象的findSubscriberMethods(subscriberClass);方法,subscriberMethodFinder这个对象呢,我们其实都不用太关心,可以把他当作一个管理获取、查找、订阅者和订阅者Method的对象,然后通过它我们就能拿到一个List集合,这个集合存储的都是SubscriberMehtod对象,这里有必要去追求一下SubscriberMehtod是个什么东西呢,我们来看看吧:

 * Copyright (C) - Markus Junginger, greenrobot (http://greenrobot.org)
package org.greenrobot.eventbus; import java.lang.reflect.Method; /** Used internally by EventBus and generated subscriber indexes. */
public class SubscriberMethod {
final Method method;
final ThreadMode threadMode;
final Class<?> eventType;
final int priority;
final boolean sticky;
/** Used for efficient comparison */
String methodString; 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();
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();
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的信息的对象,这些信息包括  final Method method(反射方法对象)对象、 final ThreadMode threadMode(线程模式)对象、final Class<?> eventType(事件类型,方法中的参数)对象、final int priority(权限) 标记、  final boolean sticky(是否sticky类型)标记、 String methodString(方法描述字串) 用来提高两个SubscriberMethod对象比较效率和准确性的。
所以这类就这么简单,就是封装了一个Mehtod的全部信息的。
 
接下来代码主线往下走。
我们来看一下findSubscriberMethods方法,是如何获取到订阅者的所有方法信息的,直接看方法。

 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;
}
}
 

从上面的代码可以看的出,一开始METHOD_CACHE这个集合是空的,获取的时候为空,逻辑会往下走,ignoreGeneratedIndex经过打印默认初始是为false的,所以下一步会走subscriberMethods = findUsingInfo(subscriberClass); OK,直接来看findUsingInfo(subscriberClass);方法。

  private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findState.subscriberInfo = getSubscriberInfo(findState);
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);
}

findState.subscriberInfo = getSubscriberInfo(findState);根据打印,获取到的为null,

所以会走  findUsingReflectionInSingleClass(findState);我们也来看看这个方法的实现。

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) {
methods = findState.clazz.getMethods();
findState.skipSuperClasses = true;
}
for (Method method : methods) {
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != && (modifiers & MODIFIERS_IGNORE) == ) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == ) {
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
Class<?> eventType = parameterTypes[];
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");
}
}
}
所以通过上面的代码,很明显就可以看出,是通过反射拿到每一个方法的修饰符信息,注解信息,参数信息,从上面的代码很明显就知道,我们的订阅方法必须有且只有一个参数,否则会抛出异常的,而且订阅方法必须是public修饰的,而且不是静态、不是抽象方法,否者也会抛出异常的,最后将合法的订阅方法都存到了一个findState.subscriberMethods集合中了,最后在findUsingReflectionInSingleClass(findState);的调用方发处调用了 
return getMethodsAndRelease(findState);返回了出去了一个List<SubscriberMethod>集合,这里需要稍微提醒一个地方就是,我们应该都发现了findUsingInfo(Class<?> subscriberClass) 方法中的倒数第四行findState.moveToSuperclass(); 看看这个方法吧.

void moveToSuperclass() {
if (skipSuperClasses) {
clazz = null;
} else {
clazz = clazz.getSuperclass();
String clazzName = clazz.getName();
/** Skip system classes, this just degrades performance. */
if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) {
clazz = null;
}
}
}
这个方法主要是获取当前Class对象的父类(有可能是抽象类或者接口),这样在findUsingInfo(Class<?> subscriberClass) 方法while中循环的获取到每一级Class对象中所有方法信息。
 
OK,findSubscriberMethods(...)方法就分析到这里,主线接着往下走,回到register方法中,往下看。
 
然后我们能看到一个for循环的遍历。

 synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
遍历每一个SubscriberMethod对象,遍历到每一个SubscriberMethod对象之后,调用EventBus的 subscribe(subscriber, subscriberMethod);方法,那我们就直接来看这个方法。

 // Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
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);
}
} int size = subscriptions.size();
for (int i = ; i <= size; i++) {
if (i == size || subscriberMethod.priority >
subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
} 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);
}
}
}

一开始就取出了eventType,然后就创建了一个Subscription对象,这里又需要解释一下这个对象是个什么玩意了,直接贴代码吧。

 * Copyright (C) - Markus Junginger, greenrobot (http://greenrobot.org)
package org.greenrobot.eventbus; final class Subscription {
final Object subscriber;
final SubscriberMethod subscriberMethod;
/**
* Becomes false as soon as {@link EventBus#unregister(Object)} is called, which is checked by queued event delivery
* {@link EventBus#invokeSubscriber(PendingPost)} to prevent race conditions.
*/
volatile boolean active; Subscription(Object subscriber, SubscriberMethod subscriberMethod) {
this.subscriber = subscriber;
this.subscriberMethod = subscriberMethod;
active = true;
} @Override
public boolean equals(Object other) {
if (other instanceof Subscription) {
Subscription otherSubscription = (Subscription) other;
return subscriber == otherSubscription.subscriber
&& subscriberMethod.equals(otherSubscription.subscriberMethod);
} else {
return false;
}
} @Override
public int hashCode() {
return subscriber.hashCode() + subscriberMethod.methodString.hashCode();
}
}

其实就是很简单的一个对象,我们可以看做一个简单的java bean就好了,他的功能就是封装了一个订阅者实体对象( final Object subscriber;)和上面我们解释过的,一个Mehtod信息的封装类(final SubscriberMethod subscriberMethod;)。

 
继续回到主线,我们可以看到,会把EventType和Subscriptions关联起来,很好理解,可以想象的到,我们Post一个事件的时候,一般会带一个事件类型,也就是post方法中的参数。
而一个EventType可能对应很多个方法和对象,然后就把他们存储起来了,源代码体现如下:

 Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
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);
}
}

然后把一个个的新new出来的Subscription添加了subscriptions中,而subscriptions又和evetType关联起来了。添加新new出来的Subscription的时候,它会根据事件方法当时声明的时候,设置的priority注解信息,当priority的放在List的队列前面。

 int size = subscriptions.size();
for (int i = ; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
再往下走,就是用实体对象和EventType关联了,这个也不难理解,我们可以理解为,一个实体对象可以写很多个订阅方法,但是没一个订阅方法可能会是不同的EventType(订阅方法的参数类型),所以最后把eventType添加到了subscribedEvents之中。

 List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);

最后一部分的代码是处理Sticky事件的逻辑,我们知道EventBus是可以调用成员方法

来postSticky(Object event)方法来提交一个事件。主要判断subscriberMethod的sticky是否为true,如果是,就从stickyEvents集合中获取所有相同事件类型的且设置了sticky属性的订阅者和订阅方法,然后就调用checkPostStickyEventToSubscription(newSubscription, stickyEvent);方法将事件发送出去了。
 
OK,到这里基本上register模块都解释的差不多了,上面我们也看到,各种事件对象、方法对象、和订阅者对象都已经封存起来,可以等待post事件的时候,将事件发送出去。

3. post(Object event) 发布订阅事件

Ok我们继续往下分析关于事件post的时候的实现原理。
我们知道我们在拿到EventBus的实例单例对象的时候,可以简单的就调用的他的post刚发,将一个特定的订阅事件类型发送出去,订阅者的订阅方法就能被回调了,我们来直接看这个方法吧。

 /** Posts the given event to the event bus. */
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(), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}

首先从currentPostingThreadState(LocalThread)中获取一个与当前线程关联的PostingThreadState对象,这个对象很简单,其实就是一个记录一个线程事件post状态的实体,可以稍微看看他的代码。

 /** For ThreadLocal, much faster to set (and get multiple values). */
final static class PostingThreadState {
final List<Object> eventQueue = new ArrayList<Object>();
boolean isPosting;
boolean isMainThread;
Subscription subscription;
Object event;
boolean canceled;
}

没错就是这么简单,其成员也很好理解,eventQueue 很明显就是一个事件队列,isPosting 这个boolean类型的标记只要就是记录当先线程的事件发布状态,isMainThread主要就是标记当前线程是否为主线程,event事件类型、canceled是否取消post。

 
首先判断状态,假如下载正在post事件的时候,就不在接受post其他事件了,我们直接分析if语句块里面的代码吧,如果事件队列为空的话,就结束post过程,方法执行结束。
否则进入postSingleEvent(eventQueue.remove(0), postingState);方法中,然后每次都是获取事件队列中的栈顶的一个事件,注意是使用的remove(0)的方式来获取,直接来看postSingleEvent(Object event, PostingThreadState postingState)方法吧。

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 = ; 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));
}
}
}

首先我们知道eventInheritance的值是为true的,所以会先调用llookupAllEventTypes(eventClass)获取eventClass所有父类的后者接口类型的class对象,存到eventTypes 集合中,这里就不贴出lookupAllEventTypes(eventClass)的代码实现了,因为他的代码很简单,主要就是想上获取父类或者接口类型的class对象,因为比如父类或者接口为Car,BMW extends Car,这些都会被获取出来,记住是eventClass的父类和接口的calss对象哦,然后挨个遍历evnetClass对象。

for (int h = ; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}

然后调用了postSingleEventForEventType(event, postingState, clazz);方法,我们直接看这个方法。

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;
}

OK,我们看到,从subscriptionsByEventType中取出一个subscriptions 对象,我们应该还有印象,前面我们讲过,说事件类型和事件订阅者关联起来了,一个订阅者可以实现多个订阅方法,接收不同事件类型,所以事件类型和订阅者关联起来, 将所有订阅者保存成集合,和事件类型关联了,Ok遍历subscriptions ,然后调用postToSubscription(subscription, event, postingState.isMainThread);方法去post事件给订阅者。我们直接看这个方法。

 private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
invokeSubscriber(subscription, event);
break;
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(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);
}
}

所以上面的代码已经很明显了,就是根据ThreadMode来在不同的线程中发布事件,第一种  case POSTING:是默认方式下的,表示在当前线程中执行,也就是在事件post所在线程中执行,我们看到它是直接调用 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);
}
}

我们看到,代码很简单,就一句话,利用发射的方式去执行了,其他的都是异常捕获。

 
接着看第二种  case MAIN:很明显是在主线程中执行的,我们看这个case下的实现方式。

if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}

很好理解,如果当前已经是在主线程中,就直接跟第一种方式一样,直接在当前线程中执行就OK了,如果当前不是主线程,那么只能通过主线程的Handler来发送handler事件消息到主线程去执行了,使用mainThreadPoster将事件加入执行队列,mainThreadPoster我们在一开始的时候我们讲过,他们的实现也很简单,这个具体怎么发送和执行可以去看看,但是他们最后都是调用EventBus的invokeSubscriber(subscription, event);方法来执行的。

 
第三种case BACKGROUND: 很明显,就是在后台子线程中执行了,看看这个case下的代码实现吧。

if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}

也很简单,直接判断当前是否为主线程,如果主线程的话,就直接使用backgroundPoster来将事件加入执行队列,

backgroundPoster我们在前面做过铺垫,他就是一个Runnable的实现体,接着调用了它自己的enqueue方法,我们稍微提一下这方法。

 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);
}
}
}

最后线程执行,就调用了

backgroundPoster的run方法。

 @Override
public void run() {
try {
try {
while (true) {
PendingPost pendingPost = queue.poll();
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) {
Log.w("Event", Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
}

他最后是通过queue.poll(1000);取出一个事件,将事件交个了EventBus中线程池来执行,从而达到了

backgroundPost的效果,也就是子线程执行的效果。
 
 
但是我们这里应该需要注意一个地方,那就是
queue.enqueue(pendingPost);这个将事件的入队操作,这里还是有必要看一下queue这个队列是个什么样的实现方式。

final class BackgroundPoster implements Runnable {

    private final PendingPostQueue queue;
private final EventBus eventBus; private volatile boolean executorRunning; BackgroundPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
} 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);
}
}
}

我们看到queue 是一个PendingPostQueue类型的实体成员变量。我们直接贴上这个类的代码来看看。

* Copyright (C) - Markus Junginger, greenrobot (http://greenrobot.org)

package org.greenrobot.eventbus;

final class PendingPostQueue {
private PendingPost head;
private PendingPost tail; synchronized void enqueue(PendingPost pendingPost) {
if (pendingPost == null) {
throw new NullPointerException("null cannot be enqueued");
}
if (tail != null) {
tail.next = pendingPost;
tail = pendingPost;
} else if (head == null) {
head = tail = pendingPost;
} else {
throw new IllegalStateException("Head present, but no tail");
}
notifyAll();
} synchronized PendingPost poll() {
PendingPost pendingPost = head;
if (head != null) {
head = head.next;
if (head == null) {
tail = null;
}
}
return pendingPost;
} synchronized PendingPost poll(int maxMillisToWait) throws InterruptedException {
if (head == null) {
wait(maxMillisToWait);
}
return poll();
} }

从上面的代码我们可以看出,poll和enqueue 都加上synchronized ,他就是模拟了一个事件队列,enqueue就是将事件入队的动作,当然还模拟了一个事件的队列的队头队尾和next,首先上面代码一开始head为和tail应该都为null,所以会执行如下代码。

            head = tail = pendingPost;
将当前队列的头尾都设置为pendingPost(当前事件),然后调用notifyAll()方法,通知线程去执行任务,就是把任务交给EventBus中声明的线程池来执行,最后调用到了run方法了,在run方法中调用了 queue.poll(1000);来一个事件,看看poll(int maxMillisToWait)方法吧。

 synchronized PendingPost poll(int maxMillisToWait) throws InterruptedException {
if (head == null) {
wait(maxMillisToWait);
}
return poll();
}

也是很简单啊,直接判断队头是否为空,如果为空就让线程先等1000毫秒,所以最后只有在enqueue被调用,加入新的事件后,notifyAll唤醒线程去调用无参数的重载方法来取出事件返回出去。

synchronized PendingPost poll() {
PendingPost pendingPost = head;
if (head != null) {
head = head.next;
if (head == null) {
tail = null;
}
}
return pendingPost;
}

方法也很简单,首先直接把head赋值给一个新的 pendingPost变量,再判断如果head不为空,直接把下一个next事件复制给head,典型的先进先出队列,然后返回pendingPost。

 
其实到这里其实也有两个很重要的知识点,队列概念的巧妙是使用,先进先出方式的使用,还有一个是生产者和消费者的概念。
回归主线,判断如果不是主线程,就刚好,直接执行事件就可以了,看最后一种
case ASYNC:
这种其实跟 BACKGROUND一样的执行方式,因为asyncPoster本身也是一个Runnable的实现体,最后也调用了enqueue方法把任务交给了EventBus中的线程池来执行,具体代码基本和BACKGROUND的分析一样的。但是这个和BACKGROUND是有区别的,它是任何时候都要在子线程中执行的.

EventBus使用详解(一)——初步使用EventBus的更多相关文章

  1. 安卓高级EventBus使用详解

    我本来想写但是在网上看了下感觉写得不如此作者写得好:http://www.jianshu.com/p/da9e193e8b03 前言:EventBus出来已经有一段时间了,github上面也有很多开源 ...

  2. EventBus使用详解(一)

    一.概述 EventBus是一款针对Android优化的发布/订阅事件总线.主要功能是替代Intent,Handler,BroadCast在Fragment,Activity,Service,线程之间 ...

  3. EventBus使用详解

    EventBus是针一款对Android的发布/订阅事件总线.它可以让我们很轻松的实现在Android各个组件之间传递消息,并且代码的可读性更好,耦合度更低.长话短说直接介绍使用. 如何使用 (以下介 ...

  4. Android之EventBus使用详解

    一.概述 当Android项目越来越庞大的时候,应用的各个部件之间的通信变得越来越复杂,例如:当某一条件发生时,应用中有几个部件对这个消息感兴趣,那么我们通常采用的就是观察者模式,使用观察者模式有一个 ...

  5. EventBus使用详解(二)——EventBus使用进阶

    一.概述 前一篇给大家装简单演示了EventBus的onEventMainThread()函数的接收,其实EventBus还有另外有个不同的函数,他们分别是: 1.onEvent2.onEventMa ...

  6. 事物总线模式实例——EventBus实例详解

    事件总线模式是一种广泛运用于安卓开发之中的一种软件架构模式,而事件总线模式在安卓开发中最广泛的应用莫过于AndroidStudio提供的EventBus,所以我就EventBus来谈谈对事件总线模式的 ...

  7. (转载)EventBus使用详解

    (转载)http://liuling123.com/2016/01/EventBus-explain.html 概述 EventBus是针一款对Android的发布/订阅事件总线.它可以让我们很轻松的 ...

  8. SqlHelper 帮助文档及详解--项目初步搭建

    微软SqlHelper类中文注释和使用方法 相关链接: http://blog.csdn.net/itmaxin/article/details/7609566 SqlHelper.cs是N年前微软出 ...

  9. EventBus (一) 使用详解——初步使用EventBus

    版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 前言:EventBus是上周项目中用到的,网上的文章大都一样,或者过时,有用的没几篇,经过琢磨,请教他人,也终于弄清楚点眉目,记 ...

随机推荐

  1. 示例Oracle 10.2.0.1.0升级到10.2.0.4.0一例

    1.查看当前系统版本 [oracle@std Disk1]$ sqlplus '/as sysdba' SQL*Plus: Release - Production on Thu Jan :: Cop ...

  2. Mysql日期时间大全

    MySQL日期时间函数大全 DAYOFWEEK(date)  返回日期date是星期几(1=星期天,2=星期一,--7=星期六,ODBC标准)mysql> select DAYOFWEEK('1 ...

  3. VC++6.0编译器标记的那些内存值

    栈内存初始值 0xcccccccc 和-858993460.   二者是一样的, 一个是16进制, 另一个是10进制

  4. 有效的PhoneGap CSS: WebKit Tap Highlight Color

    原文链接:文章1:http://phonegap-tips.com/articles/essential-phonegap-css-webkit-tap-highlight-color.html(此文 ...

  5. JTA和JDBC事务

    一般情况下,J2EE应用服务器支持JDBC事务.JTA事务.容器管理事务.这里讨论JTA和JDBC事务的区别.这2个是常用的DAO模式事务界定方式.JDBC 事务 JDBC 事务是用 Connecti ...

  6. eclipse创建web项目

    总结为3步: 必备环境: Eclipse jee Tomcat 1.创建services 2.创建dynamic web project项目 3.WebContent路径下创建index.jsp 运行 ...

  7. Android实现推送方式解决方案

    当我们开发需要和服务器交互的应用程序时,基本上都需要获取服务器端的数据,比如<地震应急通>就需要及时获取服务器上最新的地震信息. 要获取服务器上不定时更新的信息,一般来说有两种方法: 第一 ...

  8. support HTML5 in IE8

    <!--this code is used to support HTML5 in IE8--> <!--[if IE]><script type="text/ ...

  9. 孟加拉央行SWIFT攻击事件恶意木马分析(转)

    第一章 分析概述 该恶意木马样本为运行于winodws平台上的PE文件(名称:evtdiag.exe).文件大小为64KB,编译时间是2016年2月5日. 经分析,该样本为定制的攻击SWIFT客户端程 ...

  10. 土壤湿度传感器YL69使用

    1.电源:3.3V ~ 5V 2.获取湿度信息的方式(2种可同时使用): 从传感器的D0引脚:土壤湿度大于某个阈值,则D0输出0,否则输出1 从传感器的A0引脚:获取到模拟量,更加精确.土壤湿度越大, ...