首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

EventBus 3.x 源碼深度解析:從註冊到分發的完整鏈路

     編輯:Java 綜合教學

簡介

EventBus 是 Android 領域最流行的事件總線框架,通過發布-訂閱模式解耦組件通信。本文基於源碼(含中文註釋版),從構造器、註冊、發布、線程調度四個維度深度剖析其核心機制。

核心概念速覽

術語說明
Event(事件)任意對象,作為消息載體;事件類型即其 Class
Subscriber(訂閱者)註冊接收特定事件類型的對象,含優先級,可取消傳遞
Publisher(發布者)調用 EventBus.getDefault().post(event) 發送事件
ThreadMode四種執行模式:POSTING/MAIN/BACKGROUND/ASYNC

單例構造與 Builder 模式

雙重檢查鎖單例

static volatile EventBus defaultInstance;
public static EventBus getDefault() {
    if (defaultInstance == null) {
        synchronized (EventBus.class) {
            if (defaultInstance == null) defaultInstance = new EventBus();
        }
    }
    return defaultInstance;
}

EventBusBuilder 配置選項

public class EventBusBuilder {
    boolean logSubscriberExceptions = true;   // 記錄異常
    boolean logNoSubscriberMessages = true;   // 無訂閱者提示
    boolean sendSubscriberExceptionEvent = true; // 發送異常事件
    boolean sendNoSubscriberEvent = true;     // 發送無訂閱事件
    boolean throwSubscriberException = false; // 拋出異常
    boolean eventInheritance = true;          // 支持事件繼承(父類/接口)
    boolean strictMethodVerification = false; // 嚴格校驗方法簽名
    ExecutorService executorService = Executors.newCachedThreadPool(); // 線程池
    List subscriberInfoIndexes; // APT 索引支持
    
    public EventBus build() { return new EventBus(this); }
}

關鍵屬性:eventInheritance 決定是否向父類/接口傳遞;executorService 控制異步執行策略。

EventBus 核心數據結構

// 事件類型 -> 訂閱者列表(CopyOnWriteArrayList 保證線程安全遍歷)
private final Map, CopyOnWriteArrayList> subscriptionsByEventType;

// 訂閱者 -> 其訂閱的事件類型集合
private final Map>> typesBySubscriber;

// 當前線程的發布狀態(ThreadLocal 隔離)
private final ThreadLocal currentPostingThreadState = 
    ThreadLocal.withInitial(PostingThreadState::new);

PostingThreadState - 線程級發布上下文

static class PostingThreadState {
    final List eventQueue = new ArrayList<>(); // 事件隊列
    boolean isMainThread;                              // 是否主線程
    Subscription subscription;                         // 當前處理的訂閱
    Object event;                                      // 當前事件
    boolean canceled;                                  // 是否取消傳遞
}

發布流程核心鏈路

  1. post(event) → 獲取線程狀態 → 入隊 → postSingleEvent()
  2. postSingleEvent() → 若 eventInheritance 則遍歷父類/接口 → postSingleEventForEventType()
  3. postSingleEventForEventType() → 獲取訂閱列表 → 遍歷調用 postToSubscription()
  4. postToSubscription() → 根據 ThreadMode 分發

postSingleEvent - 事件繼承掃描

private void postSingleEvent(Object event, PostingThreadState postingState) {
    Class eventClass = event.getClass();
    boolean subscriptionFound = false;
    
    if (eventInheritance) {
        List> eventTypes = new ArrayList<>();
        for (Class clazz = eventClass; clazz != null; clazz = clazz.getSuperclass()) {
            eventTypes.add(clazz);
            for (Class iface : clazz.getInterfaces()) eventTypes.add(iface);
        }
        for (int h = 0, countTypes = eventTypes.size(); h < countTypes; h++) {
            Class clazz = eventTypes.get(h);
            subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
        }
    } else {
        subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
    }
    
    if (!subscriptionFound && sendNoSubscriberEvent) {
        post(new NoSubscriberEvent(this, event));
    }
}

postToSubscription - 四大線程模式實現

private void postToSubscription(Subscription sub, Object event, boolean isMainThread) {
    switch (sub.subscriberMethod.threadMode) {
        case POSTING:
            invokeSubscriber(sub, event); // 直接在發布線程執行
            break;
        case MAIN:
            if (isMainThread) invokeSubscriber(sub, event);
            else mainThreadPoster.enqueue(sub, event); // 切到主線程
            break;
        case BACKGROUND:
            if (!isMainThread) invokeSubscriber(sub, event);
            else backgroundPoster.enqueue(sub, event); // 切到後台線程池
            break;
        case ASYNC:
            asyncPoster.enqueue(sub, event); // 直接丟線程池並發
            break;
    }
}

四大 Poster 實現詳解

1. HandlerPoster - 主線程分發(MAIN 模式)

public class HandlerPoster extends Handler {
    private final PendingPostQueue queue;
    private boolean handlerActive;
    
    void enqueue(Subscription sub, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(sub, event);
        synchronized (this) {
            queue.enqueue(pendingPost);
            if (!handlerActive) {
                handlerActive = true;
                sendMessage(obtainMessage()); // 觸發 handleMessage
            }
        }
    }
    
    @Override
    public void handleMessage(Message msg) {
        long started = SystemClock.uptimeMillis();
        while (true) {
            PendingPost pendingPost = queue.poll();
            if (pendingPost == null) {
                synchronized (this) {
                    pendingPost = queue.poll();
                    if (pendingPost == null) { handlerActive = false; return; }
                }
            }
            eventBus.invokeSubscriber(pendingPost);
            // 限制單次處理時間,避免阻塞主線程過久
            if (SystemClock.uptimeMillis() - started >= maxMillisInsideHandleMessage) {
                sendMessage(obtainMessage()); // 重新調度
                return;
            }
        }
    }
}

關鍵點:關聯主線程 Looper,通過 maxMillisInsideHandleMessage(默認 10ms)分片執行,防止 ANR。

2. BackgroundPoster - 後台順序執行(BACKGROUND 模式)

final class BackgroundPoster implements Runnable {
    private final PendingPostQueue queue;
    private volatile boolean executorRunning;
    
    public void enqueue(Subscription sub, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(sub, event);
        synchronized (this) {
            queue.enqueue(pendingPost);
            if (!executorRunning) {
                executorRunning = true;
                eventBus.getExecutorService().execute(this);
            }
        }
    }
    
    @Override public void run() {
        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) { /* log */ }
        finally { executorRunning = false; }
    }
}

單線程順序處理,適合耗時但需串行的任務(如資料庫寫入)。

3. AsyncPoster - 並發異步執行(ASYNC 模式)

class AsyncPoster implements Runnable {
    private final PendingPostQueue queue;
    
    public void enqueue(Subscription sub, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(sub, event);
        queue.enqueue(pendingPost);
        eventBus.getExecutorService().execute(this); // 每個事件獨立提交任務
    }
    
    @Override public void run() {
        PendingPost pendingPost = queue.poll();
        if (pendingPost != null) eventBus.invokeSubscriber(pendingPost);
    }
}

每次 enqueue 都提交新任務到線程池,實現真正並發,適合獨立無序的耗時操作(如網絡請求、圖片加載)。

註冊與反射優化

SubscriberMethod - 訂閱方法元數據

class SubscriberMethod {
    final Method method;
    final ThreadMode threadMode;
    final Class eventType;
    final int priority;
    final boolean sticky;
    // 構造時校驗:public、非靜態、單參數、@Subscribe 註解
}

註冊流程

  1. 掃描類及父類所有方法
  2. 過濾含 @Subscribe 且符合簽名的方法
  3. 構建 SubscriberMethod 列表
  4. 為每個方法創建 Subscription,存入兩級 Map

APT 編譯時優化(SubscriberInfoIndex)

EventBus 3.0 引入註解處理器,編譯期生成索引類,運行時直接讀取避免反射掃描,大幅提升啟動速度。EventBusBuilder.ignoreGeneratedIndex 可強制禁用。

總結與最佳實踐

  • 優勢:API 簡潔、解耦徹底、線程調度靈活、支持 Sticky Event、APT 優化
  • 注意點:
    • 註冊/解註冊必須配對,否則內存洩漏
    • POSTING 模式下耗時操作會阻塞發布線程
    • MAIN 模式分片執行,單個事件處理仍需 < 10ms
    • 異常處理:默認只打印日誌,可配置 throwSubscriberException 或訂閱 SubscriberExceptionEvent
  • 替代方案:RxJava/RxAndroid(功能更強但學習曲線陡峭)、LiveData(生命周期感知,適合 View 層)

EventBus 以「約定優於配置」的設計,在 Android 事件通信領域確立了統治地位。理解其源碼機制,有助於排查疑難雜癥(如事件丟失、ANR、內存洩漏),也能在造輪子時借鑒其優雅的線程調度架構。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved