点击上方“Android技术杂货铺”,选择“置顶公众号”
干货文章,第一时间送达!
温馨提示:深度长文,请慢慢食用,或者先mark.( 文章分为上下两部分)。
本文的整体结构图
本文讲解的是 'org.greenrobot:eventbus:3.1.1' 最新版,和其他版本有细微差别,思想都是一样的。
EventBus 的简单示例
@Override
protected void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
protected void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = false, priority = 2)
public void onMessageEvent(MyBusEvent event) {
Toast.makeText(this, event.message, Toast.LENGTH_SHORT).show();
}
EventBus 的创建是一个单例模式,我们就从 getDefault() 开始讲起吧。
1.1、单例模式获取 EventBus 实例
EventBus 的创建是一个双重校验锁的单例模式。
public static EventBus getDefault() {
if (sDefaultBus == null) {
synchronized (EventBus.class) {
if (sDefaultBus == null) {
sDefaultBus = new EventBus();
}
}
}
return sDefaultBus;
}
单例模式没什么好说的,我们都知道单例模式的构造函数是私有类型 private,但是 EventBus 的构造函数却是 public 类型。
这样设计的目: EventBus 在代码使用过程中不仅仅只有一条总线,还有其他的订阅总线,订阅者可以注册到不同的 EventBus 总线,然后通过不同的 EventBus 总线发送数据。
不同的 EventBus 发送的数据是相互隔离的,订阅者只能收到注册了该 EventBus 总线内事件,而不会收到别的 EventBus 事件总线的数据。这样的设计为后面的不同环境的线程切换创造了好的条件。
/**
* Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a central bus, consider {@link #getDefault()}.
创建一个新的 EventBus 实例,每个实例在 events 事件被发送的时候都是一个单独的领域,为了使用一个 事件总线,考虑用 getDefault() 构建。
*/
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
public EventBus() {
this(DEFAULT_BUILDER);
}
这里的代码我们重点看一下 this(DEFAULT_BUILDER) 里面的 DEFAULT_BUILDER。DEFAULT_BUILDER 是一个 EventBusBuilder,从这里可以看出 EventBus 是通过 建造者模式进行构建的,接下来我们看下是如何构建的吧。
EventBus(EventBusBuilder builder) {
logger = builder.getLogger();
//Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType
subscriptionsByEventType = new HashMap<>();
//Map<Object, List<Class<?>>> typesBySubscriber
typesBySubscriber = new HashMap<>();
//Map<Class<?>, Object> stickyEvents
stickyEvents = new ConcurrentHashMap<>();
/**
用于线程间调度
**/
mainThreadSupport = builder.getMainThreadSupport();
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
//用于记录event生成索引
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
//对已经注解过的Method的查找器,会对所设定过 @Subscriber 注解的的方法查找相应的Event
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
//当调用事件处理函数发生异常是否需要打印Log
logSubscriberExceptions = builder.logSubscriberExceptions;
//当没有订阅者订阅这个消息的时候是否打印Log
logNoSubscriberMessages = builder.logNoSubscriberMessages;
//当调用事件处理函数,如果异常,是否需要发送Subscriber这个事件
sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
//当没有事件处理函数时,对事件处理是否需要发送sendNoSubscriberEvent这个标志
sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
//是否需要抛出SubscriberException
throwSubscriberException = builder.throwSubscriberException;
//与Event有继承关系的类是否都需要发送
eventInheritance = builder.eventInheritance;
//线程池 Executors.newCachedThreadPool()
executorService = builder.executorService;
}
Map<Class<?>
, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType
是以 Event 为 Key,Subscriber
为 Value,当发送这个 Event
时,可以在 Map 中找到相应的 Subscriber
。
Map<Object, List<Class<?>>> typesBySubscriber
以 Subscriber
为 Key,以 Event
为 Value,当我们注册和反注册时,都会操作 typesBySubscriber
这个 Map
。
Map<Class<?>, Object> stickyEvents
是管理 EventBus
的粘性事件,粘性事件的 Event
发送出去之后,即使负责接收的 Subscriber
没有注册,当注册之后,依然能收到该事件,和广播接受者的粘性事件类似。
mainThreadPoster
:主线程的 Poster
mainThreadSupport.createPoster(this)
返回的是HandlerPoster(eventBus, looper, 10)
,looper
是一个 MainThread
的 Looper
。意味着这个 HandlerPoster
可能就是 Handler
的实现。
backgroundPoster
:后台线程的 Poster
asyncPoster
:异步线程的 Poster
HandlerPoster 其实就是 Handler 的实现,内部维护了一个 PendingPostQueue 的消息队列,在 enqueue(Subscription subscription, Object event) 方法中不断从 pendingPostPool 的 ArrayList 缓存池中获取 PendingPost 添加到 PendingPostQueue 队列中,并将该 PendingPost 事件发送到 Handler 中处理。
在 handleMessage 中,通过一个 while 死循环,不断从 PendingPostQueue 中取出 PendingPost 出来执行,获取到 post 之后由 eventBus 通过该 post 查找相应的 Subscriber 处理事件。
while 退出的条件有两个
获取到的 PendingPost 为 null,即是 PendingPostQueue 已经没有消息可处理。
每个 PendingPost 在 Handler 中执行的时间超过了最大的执行时间。
HandlerPoster UML 类图
HandlerPoster 执行过程
public class HandlerPoster extends Handler implements Poster {
private final PendingPostQueue queue;//存放待执行的 Post Events 的事件队列
private final int maxMillisInsideHandleMessage;//Post 事件在 handleMessage 中执行的最大的时间值,超过这个时间值则会抛异常
private final EventBus eventBus;
private boolean handlerActive;//标识 Handler 是否被运行起来
protected HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
super(looper);
this.eventBus = eventBus;
this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
queue = new PendingPostQueue();
}
public void enqueue(Subscription subscription, Object event) {
//从 pendingPostPool 的 ArrayList 缓存池中获取 PendingPost 添加到 PendingPostQueue 队列中,并将该 PendingPost 事件发送到 Handler 中处理。
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);//添加到队列中
if (!handlerActive) {//标记 Handler 为活跃状态
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 队列中取出 post 事件执行
PendingPost pendingPost = queue.poll();
if (pendingPost == null) {//如果为 null,表示队列中没有 post 事件,此时标记 Handelr 关闭,并退出 while 循环
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
handlerActive = false; //标记 Handler 为非活跃状态
return;
}
}
}
//获取到 post 之后由 eventBus 通过该 post 查找相应的 Subscriber 处理事件
eventBus.invokeSubscriber(pendingPost);
//计算每个事件在 handleMessage 中执行的时间
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;
}
}
}
final class PendingPost {
//通过ArrayList来实现PendingPost的添加和删除
private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();
Object event;
Subscription subscription;
PendingPost next;
private PendingPost(Object event, Subscription subscription) {
this.event = event;
this.subscription = subscription;
}
//获取 PendingPost
static PendingPost obtainPendingPost(Subscription subscription, Object event) {
synchronized (pendingPostPool) {
int size = pendingPostPool.size();
if (size > 0) {
PendingPost pendingPost = pendingPostPool.remove(size - 1);
pendingPost.event = event;
pendingPost.subscription = subscription;
pendingPost.next = null;
return pendingPost;
}
}
return new PendingPost(event, subscription);
}
//释放 PendingPost
static void releasePendingPost(PendingPost pendingPost) {
pendingPost.event = null;
pendingPost.subscription = null;
pendingPost.next = null;
synchronized (pendingPostPool) {
// Don't let the pool grow indefinitely
if (pendingPostPool.size() < 10000) {
pendingPostPool.add(pendingPost);
}
}
}
}
BackgroundPoster
实现了 Runnable
和 Poster
,enqueue()
和 HandlerPoster
中实现一样,在上文中已经讲过,这里不再赘述。
我们来看下 run()
方法中的实现,不断从 PendingPostQueue
中取出 pendingPost
到 EventBus
中分发,这里注意外部是 while() 死循环,意味着 PendingPostQueue
中所有的 pendingPost
都将分发出去。而 AsyncPoster
只是取出一个。
final class BackgroundPoster implements Runnable, Poster {
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;
//在线程池中执行这个 pendingPost
eventBus.getExecutorService().execute(this);//Executors.newCachedThreadPool()
}
}
}
@Override
public void run() {
try {
try {
//不断循环从 PendingPostQueue 取出 pendingPost 到 eventBus 执行
while (true) {
//在 1000 毫秒内从 PendingPostQueue 中获取 pendingPost
PendingPost pendingPost = queue.poll(1000);
//双重校验锁判断 pendingPost 是否为 null
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();//再次尝试获取 pendingPost
if (pendingPost == null) {
executorRunning = false;
return;
}
}
}
//将 pendingPost 通过 EventBus 分发出去
//这里会将PendingPostQueue中【所有】的pendingPost都会分发,这里区别于AsyncPoster
eventBus.invokeSubscriber(pendingPost);
}
} catch (InterruptedException e) {
eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
}
}
class AsyncPoster implements Runnable, Poster {
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);//Executors.newCachedThreadPool()
}
@Override
public void run() {
PendingPost pendingPost = queue.poll();
if(pendingPost == null) {
throw new IllegalStateException("No pending post available");
}
eventBus.invokeSubscriber(pendingPost);
}
}
当我们指定订阅方法的时候,会在方法上加上注解,如下,下面我们看看这个注解的具体含义
@Subscribe(threadMode = ThreadMode.MAIN, sticky = false, priority = 2)
public void onMessageEvent(EventTest event) {
}
@Documented //命名为 java doc 文档
@Retention(RetentionPolicy.RUNTIME) //指定在运行时有效,即在运行时能保持这个 Subscribe
@Target({ElementType.METHOD}) //指定类型为 METHOD,表名用来描述方法
public @interface Subscribe {
//指定线程模式,可以指定在 Subscribe 中接收的 Event 所处的线程
ThreadMode threadMode() default ThreadMode.POSTING;
boolean sticky() default false;
int priority() default 0;
}
public enum ThreadMode {
POSTING,//EventBus 默认的线程模式
MAIN,//主线程
MAIN_ORDERED,//主线程
BACKGROUND,//后台线程
ASYNC//异步线程
}
ThreadMode 是 enum(枚举)类型,threadMode 默认值是 POSTING。3.1.1 版本的 EventBus 新增了一种类型,共 5 种,以前的版本是 4 种。
POSTING:事件发布在什么线程,就在什么线程订阅。故它不需要切换线程来分发事件,因此开销最小。它要求 task 完成的要快,不能请求 MainThread,适用简单的 task。
MAIN:无论事件在什么线程发布,都在主线程订阅。事件将排队等待交付(非阻塞)。使用此模式的订阅者必须快速返回,以避免阻塞主线程。
MAIN_ORDERED:无论事件在什么线程发布,都在主线程订阅。区别于 MAIN,MAIN_ORDERED 事件将始终排队等待交付。这将确保 post 调用是非阻塞的。
BACKGROUND:如果发布的线程不是主线程,则在该线程订阅,如果是主线程,则使用一个单独的后台线程订阅。
ASYNC:在非主线程和发布线程中订阅。当处理事件的 Method 是耗时的,需要使用此模式。尽量避免同时触发大量的耗时较长的异步操作,EventBus 使用线程池高效的复用已经完成异步操作的线程。
粘性事件是事件消费者在事件发布之后才注册,依然能接收到该事件的特殊类型。
StickyEvent 与普通 Event的 普通就在于,EventBus 会自动维护被作为 StickyEvent 被 post 出来(即在发布事件时使用 EventBus.getDefault().postSticky(new MyEvent()) 方法)的事件的最后一个副本在缓存中。 任何时候在任何一个订阅了该事件的订阅者中的任何地方,都可以通 EventBus.getDefault().getStickyEvent(MyEvent.class)来取得该类型事件的最后一次缓存。
sticky(粘性)默认值是 false,如果是 true,那么可以通过 EventBus 的 postSticky 方法分发最近的粘性事件给该订阅者(前提是该事件可获得)。
priority 是 Method 的优先级,优先级高的可以先获得分发事件的权利。这个不会影响不同的 ThreadMode 的分发事件顺序。
剩下4、5、6、7节将在下篇分享
作者:蒋志碧
链接:https://www.jianshu.com/p/a3cdf5add8b
著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
往期干货
2
3
如果你觉得本文对你有帮助,请分享给更多的人
关注【Android技术杂货铺】,每天都有Android 干货文章分享!
长按识别二维码关注