##EventBus3.x源码分析之发送事件
发送事件时,最长使用的就是EventBus.getDefault().post(Object object)方法。
这里只分析非粘性事件发送,因为粘性事件已经再EventBus3.x源码分析之注册(一)中提到过,后续的逻辑同post。
EventBus#
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List
1、发送事件的入口函数,通过ThreadLocal的方式获取PostingThreadState实例,好处就是将每个需要它的线程分配一份实例,保证数据的安全及速度。
2、并将事件存入发送事件列表eventQueue。判断当前事件是否已经被发送了,进行一系列发送事件状态封装。
3、然后轮询获取事件列表中的事件,直到取完为止。最后将PostingThreadState实例重置。
EventBus#
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class eventClass = event.getClass();
//是否找到了对应的订阅信息
boolean subscriptionFound = false;
//是否需要查找发送的事件的父类以及接口
if (eventInheritance) {
List> 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);
}
...
}
private static List> lookupAllEventTypes(Class eventClass) {
synchronized (eventTypesCache) {
List> 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;
}
}
发送事件前,找到对应的事件类型。
EventBus#
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class eventClass) {
CopyOnWriteArrayList 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);
//正常情况下者里的postingState.canceled返回一定是false,不会终止,但是如果有中途中断比如#cancelEventDelivery(Object event)会终止发送事件。
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
//如果调用的中断方法,终止后续的发送事件
if (aborted) {
break;
}
}
return true;
}
return false;
}
//发送事件的状态
final static class PostingThreadState {
final List eventQueue = new ArrayList<>();//发送事件列表
boolean isPosting;//是否已经发送了
boolean isMainThread;//是否主线程
Subscription subscription;
Object event;
boolean canceled;//是否需要终止发送
}
根据订阅类型,将所有之前订阅过的订阅信息取出,循环发送事件。
ThreadMode#
public enum ThreadMode {
//默认模式,事件的处理在和事件的发送在相同的进程,所以事件处理时间不应太长,不然影响事件的发送线程,而这个线程可能是UI线程.
/**
* Subscriber will be called directly in the same thread, which is posting the event. This is the default. Event delivery
* implies the least overhead because it avoids thread switching completely. Thus this is the recommended mode for
* simple tasks that are known to complete in a very short time without requiring the main thread. Event handlers
* using this mode must return quickly to avoid blocking the posting thread, which may be the main thread.
*/
POSTING,
//事件处理会在主线程
//如果分发事件在主线程,会在主线程立即处理事件;
//如果事件分发在后台线程,事件将被加入队列排队分发(不会堵塞);
//所以事件处理不应太长时间,否则造成主线程处理事件阻塞。
/**
* On Android, subscriber will be called in Android's main thread (UI thread). If the posting thread is
* the main thread, subscriber methods will be called directly, blocking the posting thread. Otherwise the event
* is queued for delivery (non-blocking). Subscribers using this mode must return quickly to avoid blocking the main thread.
* If not on Android, behaves the same as {@link #POSTING}.
*/
MAIN,
//同MAIN,但是分发线程都会加入队列排队分发(不会造成分发堵塞)。
/**
* On Android, subscriber will be called in Android's main thread (UI thread). Different from {@link #MAIN},
* the event will always be queued for delivery. This ensures that the post call is non-blocking.
*/
MAIN_ORDERED,
// 事件的处理在后台线程
// 如果事件分发在后台线程,事件会立即在后台线程执行处理;
// 如果事件分发在主线程,事件会被加到一个队列中,从线程池取出一条线程进行(有序)处理;如此设计的方式也是为了保证优先级的可用
// 所以事件处理时间不应太长,否则会阻塞后台线程(一条线程处理不过来。。)。
/**
* On Android, subscriber will be called in a background thread. If posting thread is not the main thread, subscriber methods
* will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single
* background thread, that will deliver all its events sequentially. Subscribers using this mode should try to
* return quickly to avoid blocking the background thread. If not on Android, always uses a background thread.
*/
BACKGROUND,
//事件处理会在单独的线程中执行,主要用于在后台线程中执行耗时操作
//无论事件分发在哪个线程,事件处理都会从线程池中取出线程进行处理(并发处理)。
/**
* Subscriber will be called in a separate thread. This is always independent from the posting thread and the
* main thread. Posting events never wait for subscriber methods using this mode. Subscriber methods should
* use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number
* of long running asynchronous subscriber methods at the same time to limit the number of concurrent threads. EventBus
* uses a thread pool to efficiently reuse threads from completed asynchronous subscriber notifications.
*/
ASYNC
}
这里就是为何要在定义订阅方法的时候要指定@Subscribe(threadMode = ThreadMode.xxx)。当然不指定默认为POSTTING,下面会做详细分析。
EventBus#
//事件入列,或处理
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 MAIN_ORDERED:
//如果是在Android当中
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:
当前线程分发,当前线程处理,也不需要队列。
####MAIN:
1、如果主线程分发,处理也在主线程,也不需要队列。
2、如果子线程分发,需要先入列。
分析代码mainThreadPoster是HandlerPoster的实例
HandlerPoster#
@Override
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");
}
}
}
}
首先通过obtainPendingPost获取PendingPost实例。
//待分发的事件封装体
final class PendingPost {
//对象池——所有分发器公用
private final static List pendingPostPool = new ArrayList();
//事件链表
PendingPost next;
...
//从对象池中获取分发事件
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);
}
//释放事件中的属性
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);
}
}
}
}
这里维护了一个公所有分发器使用的待分发事件对象池,取出一个删除一个,释放时候添加一个。上限10000
//待分发事件管理队列
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();
}
...
}
拿到带分发事件实例后,进行入列。
1、PendingPostQueue这个对象有两个成员全部是PendingPost类型,但顾名思义是“首”“尾”意思,而PendingPost中也有一个自身类型的成员为next,这就构成了事件链表。
2、第一个事件刚入列,首尾都是该事件,后面来的,添加给尾部事件的成员next上,这就构成链表。
HandlerPoster#
@Override
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
while (true) {
PendingPost pendingPost = queue.poll();//去取事件
//事件取完后就会将handlerActive置为false。
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;
//如果处理事件(循环)的时间>10ms,终止此队列的消息处理。
if (timeInMethod >= maxMillisInsideHandleMessage) {
//并再次发送消息,执行handlemessage,继续之前队列中事件的处理,保证主线程阻塞时间不超过10ms
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
//并将handlerActive置为true,防止多次发送消息。
rescheduled = true;
return;
}
}
} finally {
handlerActive = rescheduled;
}
}
在事件入列发送空消息后,会回调HandlerPoster的handleMessage方法
//待分发事件管理队列
final class PendingPostQueue {
...
synchronized PendingPost poll() {
PendingPost pendingPost = head;
//取出第一个事件,并准备下一个事件
if (head != null) {
head = head.next;
if (head == null) {
tail = null;
}
}
return pendingPost;
}
...
}
1、然后循环从队列中取出事件,并通过eventBus.invokeSubscriber(pendingPost);将事件拉到主线程进行处理。
2、这里维护了一个时间差,因为循环处理事件会造成线程堵塞,所以这里实行超过maxMillisInsideHandleMessage这个时间,终止此队列消息处理,再次发送空消息,继续上次队列中的时间处理。
####MAIN_ORDERED:
分析这里,mainThreadPoster是否为空,取决于mainThreadSupport=builder.getMainThreadSupport();是否返回null;
最终会走到
Object getAndroidMainLooperOrNull() {
try {
return Looper.getMainLooper();
} catch (RuntimeException e) {
// Not really a functional Android (e.g. "Stub!" maven dependencies)
return null;
}
}
所以这里通过mainThreadPoster != null可以判断当前试运行在Android系统中。
这种模式会将事件添加到分发队列
针对MAIN和MAIN_ORDERED:这两个分发不会阻塞因为有队列,但是处理事件在主线程有可能造成堵塞,所以建议处理的事件不要耗时。
####TBACKGROUND:
1、如果分发在子线程,当即处理事件。
2、如果分发在主线程
分析代码backgroundPoster是BackgroundPoster的实例
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;
}
}
队列的逻辑与先前分析的无差别,但是事件处理不同,这里使用了线程池,而且是只是用了一条线程。之前讲过为了保证处理事件的有序。
####ASYNC
无论分发在哪个线程,将事件添加到分发队列,通过线程池,并发处理这些事件
分析代码asyncPoster是AsyncPoster的实例
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);
}
@Override
public void run() {
PendingPost pendingPost = queue.poll();
if(pendingPost == null) {
throw new IllegalStateException("No pending post available");
}
eventBus.invokeSubscriber(pendingPost);
}
}
这里的逻辑是入列一个事件开一条线程取处理个事件。并发执行。这也必然会导致@Subscribe(pority = 1)这个优先级失效!
线程池使用的是无限大可缓存的线程池。
//使用系统默认线程池
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
以上就是分发事件流程。