爆肝揭秘!Android EventBus后台线程分发模块底层原理全解析
一、引言
在Android应用开发中,多线程编程是提升应用性能和用户体验的关键技术。EventBus作为一款经典的事件驱动框架,其后台线程分发模块在处理耗时任务、避免主线程阻塞方面发挥着核心作用。当我们需要执行网络请求、文件读写、数据计算等耗时操作时,将事件分发到后台线程处理是最佳实践。本文将深入EventBus源码,逐行解析后台线程分发模块的工作原理,帮助开发者彻底掌握其运行机制,从而在实际开发中灵活运用,打造更流畅、高效的应用程序。
二、EventBus基础概念回顾
2.1 EventBus核心功能概述
EventBus基于发布-订阅(Publish-Subscribe)模式,实现了组件间的解耦通信。其核心功能包括:
- 事件发布:任何组件都可以通过EventBus发布事件对象
- 事件订阅:组件通过注解标记订阅方法,实现对特定事件的监听
- 事件分发:EventBus负责将发布的事件分发给对应的订阅者
- 线程管理:支持在不同线程模式下处理事件,包括主线程、后台线程等
2.2 线程模式简介
EventBus支持多种线程模式,其中与后台处理相关的主要有:
BACKGROUND:确保事件在后台线程处理ASYNC:将事件放入独立的异步线程处理POSTING:在事件发布的线程中处理(如果是后台线程发布,则直接在该线程处理)
这些线程模式为开发者提供了灵活的选择,根据不同的业务场景选择合适的线程模式,可以有效提升应用性能。
三、后台线程分发模块核心概念
3.1 为什么需要后台线程分发
在Android开发中,主线程(UI线程)主要负责界面渲染和用户交互。如果在主线程执行耗时操作,会导致界面卡顿甚至ANR(Application Not Responding)。因此,对于以下场景需要使用后台线程:
- 网络请求
- 数据库操作
- 文件读写
- 复杂数据计算
EventBus的后台线程分发模块,正是为了解决这些场景下的线程调度问题。
3.2 后台线程分发流程
后台线程分发的核心流程如下:
- 事件发布:调用EventBus的
post()方法发布事件 - 线程模式判断:EventBus根据订阅方法的线程模式进行判断
- 事件入队:将事件放入后台线程队列
- 线程处理:后台线程从队列中取出事件并执行订阅方法
四、源码级深度解析
4.1 EventBus类关键代码分析
4.1.1 核心成员变量
public class EventBus {
// 事件类型与订阅者信息的映射表
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
// 后台线程处理器
private final BackgroundPoster backgroundPoster;
// 异步线程处理器
private final AsyncPoster asyncPoster;
// 其他成员变量...
private EventBus(EventBusBuilder builder) {
// 初始化事件映射表
subscriptionsByEventType = new HashMap<>();
// 创建后台线程处理器
backgroundPoster = new BackgroundPoster(this);
// 创建异步线程处理器
asyncPoster = new AsyncPoster(this);
// 其他初始化操作...
}
}
上述代码中,subscriptionsByEventType用于存储事件类型与订阅者的映射关系。backgroundPoster和asyncPoster分别负责BACKGROUND和ASYNC线程模式的事件处理。
4.1.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;
try {
while (!eventQueue.isEmpty()) {
// 逐个处理事件
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
// 重置状态
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
post()方法首先将事件加入当前线程的事件队列,然后通过postSingleEvent()方法处理每个事件。
4.1.3 postSingleEvent()方法
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
// 获取事件类型
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
// 查找所有匹配的事件类型
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
for (Class<?> clazz : eventTypes) {
// 分发事件
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));
}
}
}
该方法负责查找匹配的订阅者,并调用postSingleEventForEventType()进行实际的事件分发。
4.1.4 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()将事件分发给每个订阅者。
4.1.5 postToSubscription()方法
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
// 在当前线程处理
invokeSubscriber(subscription, event);
break;
case MAIN:
// 主线程处理逻辑...
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);
}
}
该方法根据线程模式进行不同处理。当线程模式为BACKGROUND且当前是主线程时,会将事件交给backgroundPoster处理;当线程模式为ASYNC时,会将事件交给asyncPoster处理。
4.2 BackgroundPoster类解析
4.2.1 类定义与成员变量
class BackgroundPoster implements Runnable, Poster {
// 事件队列
private final PendingPostQueue queue;
// EventBus实例
private final EventBus eventBus;
// 线程执行状态
private volatile boolean executorRunning;
BackgroundPoster(EventBus eventBus) {
this.eventBus = eventBus;
// 初始化事件队列
queue = new PendingPostQueue();
}
}
BackgroundPoster实现了Runnable接口,用于在后台线程处理事件。queue用于存储待处理的事件,executorRunning用于标记线程是否正在执行。
4.2.2 enqueue()方法
@Override
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);
}
}
}
enqueue()方法将事件加入队列,并在必要时启动后台线程。
4.2.3 run()方法
@Override
public void run() {
try {
try {
while (true) {
// 从队列中取出事件
PendingPost pendingPost = queue.poll(1000);
if (pendingPost == null) {
synchronized (this) {
// 再次检查队列
pendingPost = queue.poll();
if (pendingPost == null) {
// 队列为空,结束线程
executorRunning = false;
return;
}
}
}
// 执行事件处理
eventBus.invokeSubscriber(pendingPost);
}
} catch (InterruptedException e) {
// 处理中断异常
Thread.currentThread().interrupt();
}
} finally {
executorRunning = false;
}
}
run()方法是后台线程的执行体,不断从队列中取出事件并调用eventBus.invokeSubscriber()进行处理。
4.3 AsyncPoster类解析
4.3.1 类定义与成员变量
class AsyncPoster implements Runnable, Poster {
// 事件队列
private final PendingPostQueue queue;
// EventBus实例
private final EventBus eventBus;
AsyncPoster(EventBus eventBus) {
this.eventBus = eventBus;
// 初始化事件队列
queue = new PendingPostQueue();
}
}
AsyncPoster同样实现了Runnable接口,用于异步处理事件。
4.3.2 enqueue()方法
@Override
public void enqueue(Subscription subscription, Object event) {
// 创建待处理事件对象
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
// 将事件加入队列
queue.enqueue(pendingPost);
// 启动异步线程处理
eventBus.getExecutorService().execute(this);
}
enqueue()方法将事件加入队列后,立即启动异步线程进行处理。
4.3.3 run()方法
@Override
public void run() {
// 从队列中取出事件
PendingPost pendingPost = queue.poll();
if (pendingPost != null) {
// 执行事件处理
eventBus.invokeSubscriber(pendingPost);
} else {
throw new IllegalStateException("No pending post available");
}
}
run()方法从队列中取出事件并进行处理,与BackgroundPoster的处理逻辑有所不同。
4.4 PendingPost类解析
4.4.1 类定义与成员变量
final class PendingPost {
// 指向下一个PendingPost对象
private static PendingPost pool;
// 下一个节点
PendingPost next;
// 订阅者信息
final Subscription subscription;
// 事件对象
final Object event;
private PendingPost(Subscription subscription, Object event) {
this.subscription = subscription;
this.event = event;
}
}
PendingPost用于封装订阅者信息和事件对象,采用对象池技术提高性能。
4.4.2 对象池管理方法
static PendingPost obtainPendingPost(Subscription subscription, Object event) {
synchronized (PendingPost.class) {
if (pool != null) {
// 从对象池获取对象
PendingPost pendingPost = pool;
pool = pendingPost.next;
pendingPost.next = null;
pendingPost.subscription = subscription;
pendingPost.event = event;
return pendingPost;
}
}
// 创建新对象
return new PendingPost(subscription, event);
}
static void releasePendingPost(PendingPost pendingPost) {
// 清空数据
pendingPost.event = null;
pendingPost.subscription = null;
synchronized (PendingPost.class) {
// 归还到对象池
pendingPost.next = pool;
pool = pendingPost;
}
}
obtainPendingPost()方法用于从对象池获取PendingPost对象,releasePendingPost()方法用于将使用后的对象归还到对象池。
4.5 PendingPostQueue类解析
4.5.1 类定义与成员变量
final class PendingPostQueue {
// 队列头节点
private PendingPost head;
// 队列尾节点
private PendingPost tail;
}
PendingPostQueue使用链表结构实现线程安全的队列。
4.5.2 队列操作方法
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();
}
enqueue()方法用于将事件加入队列,poll()方法用于从队列中取出事件,poll(int maxMillisToWait)方法支持带超时的取出操作。
五、使用示例
5.1 定义事件类
public class DataLoadEvent {
private List<String> data;
public DataLoadEvent(List<String> data) {
this.data = data;
}
public List<String> getData() {
return data;
}
}
5.2 定义订阅者
public class MySubscriber {
// 后台线程处理
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onDataLoad(DataLoadEvent event) {
// 模拟耗时操作
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
List<String> data = event.getData();
// 处理数据
}
// 异步线程处理
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onAsyncTask(AsyncEvent event) {
// 执行异步任务
}
}
5.3 事件发布
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 注册订阅者
EventBus.getDefault().register(this);
// 发布后台线程事件
List<String> data = new ArrayList<>();
DataLoadEvent event = new DataLoadEvent(data);
EventBus.getDefault().post(event);
// 发布异步线程事件
AsyncEvent asyncEvent = new AsyncEvent();
EventBus.getDefault().post(asyncEvent);
}
@Override
protected void onDestroy() {
super.onDestroy();
// 取消注册
EventBus.getDefault().unregister(this);
}
}
六、性能优化与注意事项
6.1 性能优化建议
- 合理使用线程池:EventBus默认使用的线程池可以通过配置进行优化,避免线程创建和销毁的开销
- 减少对象创建:利用PendingPost的对象池机制,避免频繁创建对象
- 批量处理:如果可能,将多个相关事件合并处理,减少线程切换次数