EventBus的使用及原理解析

EventBus 是一款在 Android 開發中使用的發佈/訂閱事件總線框架,基於觀察者模式,將事件的接收者和發送者分離,避免複雜且容易出錯的依賴關係和生命週期問題,簡化了組件之間的通信,使用簡單、效率高、體積小!下邊是官方的 EventBus 原理圖:

一、使用EventBus

EventBus支持訂閱者方法在不同於發佈事件所在線程的線程中被調用。你可以使用線程模式來指定調用訂閱者方法的線程。這就需要了解EventBus支持的五種線程模式(ThreadMode):POSTING、MAIN、MAIN_ORDERED、BACKGROUND、ASYNC。

1.線程模式

  • POSTING
    POSTING是默認線程模式,在哪個線程發送事件就在對應線程處理事件,避免了線程切換,效率高。因此,對於不要求是主線程並且耗時很短的簡單任務推薦使用該模式。在線程模型爲POSTING的事件處理函數中儘量避免執行耗時操作,因爲它會阻塞事件的傳遞,甚至有可能會引起ANR。
  • MAIN
    訂閱者方法將在主線程(UI線程)中被調用。因此,可以在該模式的訂閱者方法中直接更新UI界面。事件處理的時間不能太長,長了會導致ANR。
  • MAIN_ORDERED
    訂閱者在主線程中調用。該事件總是排隊等待以後交付給訂閱者,因此對post的調用將立即返回。這爲事件處理提供了更嚴格且更一致的順序,例如,在具有MAIN線程模式的事件處理程序中發佈另一個事件,則第二個事件處理程序將在第一個事件處理程序之前完成,事件處理的時間不能太長,長了會導致ANR。
  • BACKGROUND
    訂閱者將在後臺線程中調用。如果發佈線程不是主線程,則將在發佈線程中直接調用事件處理程序方法。如果發佈線程是主線程,則EventBus使用單個後臺線程,該線程將按順序傳遞其所有事件。在此事件處理函數中禁止進行UI更新操作。
  • ASYNC
    無論事件在哪個線程中發佈,該事件處理函數都會在新建的子線程中執行;同樣,此事件處理函數中禁止進行UI更新操作。

2.基本使用

EventBus的使用分4步
(1)定義事件

public static class MessageEvent {... }

(2)準備訂閱者(指定模式)

@Subscribe(threadMode = ThreadMode.MAIN)  
public void onMessageEvent(MessageEvent event) {...};

(3)註冊/註銷訂閱者(註冊和註銷是成對出現的)

 @Override
 public void onStart() {
     super.onStart();
     EventBus.getDefault().register(this);
 }
 @Override
 public void onStop() {
     super.onStop();
     EventBus.getDefault().unregister(this);
 }

(4)發送事件

EventBus.getDefault().post(new MessageEvent());

3.使用示例

前面已經介紹了基本用法,下面看下使用示例:
首先配置gradle,如下所示:

implementation 'org.greenrobot:eventbus:3.1.1'

(1)定義消息事件類

public class MessageEvent {
    private String message;

    public MessageEvent(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

(2)訂閱者處理事件

   @Subscribe(threadMode = ThreadMode.MAIN)
    public void onShowEventMessage(MessageEvent messageEvent){
        //將數據顯示到頁面上
        tvMessage.setText(messageEvent.getMessage());
    }

我們在MainActivity中創建一個訂閱者處理事件onShowEventMessage方法,添加Subscribe註解,ThreadMode設置爲MAIN,事件的處理會在UI線程中執行,用TextView來展示接收到的消息。

(3)註冊和取消訂閱事件

public class MainActivity extends AppCompatActivity {

    private Button btnJump;
    private Button btnReg;
    private TextView tvMessage;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnJump = findViewById(R.id.btn_jump);
        btnReg = findViewById(R.id.btn_reg);
        tvMessage = findViewById(R.id.tv_message);
        btnJump.setOnClickListener(v -> {
            //跳轉到SecondActivity
            startActivity(new Intent(MainActivity.this,SecondActivity.class));
        });
        btnReg.setOnClickListener(v -> {
            //註冊事件
            EventBus.getDefault().register(this);
        });
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //註銷事件
        EventBus.getDefault().unregister(this);
    }
}

我們在MainAtivity中點擊按鈕註冊事件,在onDestroy中註銷事件。當我們註冊事件後,跳轉到SecondActivity中去發佈事件。

(4)發佈者發佈事件

public class SecondActivity extends AppCompatActivity {

    private Button btnSend;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        btnSend = findViewById(R.id.btn_send);
        btnSend.setOnClickListener(v -> {
            //發送事件
            EventBus.getDefault().post(new MessageEvent("我要學習EventBus"));
            finish();
        });

    }
}

在SecondActivity中點擊按鈕發佈事件後,返回到MainActivity中,可以看到頁面上顯示的就是我們發佈的消息“我要學習EventBus”。在這裏就不展示頁面了。
EventBus的粘性事件
除了普通事件,EventBus還支持發送粘性事件,粘性事件就是在發送事件之後再訂閱該事件也能收到該事件,這跟粘性廣播類似。
(1)訂閱者處理粘性事件

 @Subscribe(threadMode = ThreadMode.POSTING, sticky = true)
    public void onShowStickyEventMessage(MessageEvent messageEvent){
        //顯示粘性事件
        tvMessage.setText(messageEvent.getMessage());
    }

在MainActivity中新寫一個方法用來處理粘性事件,可以看到和普通事件基本類似,在註解中多加了一個sticky = true屬性。我們先不註冊事件,直接跳轉到MainActivity中,回來後在點擊註冊事件,看看效果。
(2)發送粘性事件

 btnSend.setOnClickListener(v -> {
            //發送事件
            EventBus.getDefault().postSticky(new MessageEvent("我要學習EventBus的粘性事件"));
            finish();
        });

我們在SecondActivity中點擊按鈕發送粘性事件,我們可以看到普通事件是post()方法,而粘性事件是postSticky()方法,這點需要注意。再者,我們發送後,回到MainActivity中點擊註冊事件,我們就可以看到發送的事件了。

三、源碼解析EventBus

下面我們看下EventBus的源碼

1.註冊訂閱者

1.1EventBus構造方法
 EventBus.getDefault().register(this);

我們在使用EventBus時,首先會調用EventBus.getDefault()來獲取實例,其中getDefault()是一個單例方法,保證當前只有一個EventBus實例:

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

這個採用了雙重檢查模式的單例模式,下面看下EventBus的構造方法做了什麼事情:

  public EventBus() {
        this(DEFAULT_BUILDER);
    }

this調用了EventBus的另一個構造方法。而DEFAULT_BUILDER是默認的EventBusBuilder ,用來構造EventBus。

 private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();

又創建EventBusBuilder對象,構造一個EventBusBuilder來對EventBus進行配置。

    EventBus(EventBusBuilder builder) {
        logger = builder.getLogger();
        subscriptionsByEventType = new HashMap<>();
        typesBySubscriber = new HashMap<>();
        stickyEvents = new ConcurrentHashMap<>();
        //主線程初始化操作
        mainThreadSupport = builder.getMainThreadSupport();
        mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
        //子線程初始化操作
        backgroundPoster = new BackgroundPoster(this);
        asyncPoster = new AsyncPoster(this);
        //得到訂閱信息的數量
        indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
        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;
    }

這裏採用了建造者模式。當然我們也可以通過配置EventBusBuilder來更改EventBus的屬性,如下方式也可以註冊:

EventBus.builder()
        .eventInheritance(false)
        .logSubscriberExceptions(false)
        .build()
        .register(this);

下面我們看下register方法

    public void register(Object subscriber) {
        //獲取類 通過debug可以看出獲取出來的結果是com.monkey.eventbus.MainActivity
        Class<?> subscriberClass = subscriber.getClass();
        //找出傳進來的訂閱者的所有訂閱方法
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        //進行同步操作
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

可以看到register()方法主要分爲查找和註冊兩部分。

1.2查找訂閱者的訂閱方法

我們先看下SubscriberMethod都包含了什麼:

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

我們可以看到SubscriberMethod 類中主要保存了訂閱方法的Method對象,線程模式、事件類型、優先級、是否是粘性事件

下面來看查找的過程,findSubscriberMethods()方法:

 List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        //從緩存中查找是否有訂閱方法,返回一個集合
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        //如果緩存中存在訂閱方法,則直接返回
        if (subscriberMethods != null) {
            return subscriberMethods;
        }
        //根據 ignoreGeneratedIndex屬性值來選擇採用何種方法查找訂閱方法
        //ignoreGeneratedIndex默認爲false,是否忽略註解器生成的
        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;
        }
    }

findSubscriberMethods()流程很清晰,即先從緩存中查找,如果找到則直接返回,否則去做下一步的查找過程,然後緩存查找到的集合。我們在項目中經常通過EventBus單例模式來獲取默認的EventBus對象,也就是ignoreGeneratedIndex爲false的情況,這種情況會調用findUsingInfo()方法:

    private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        //FindState其實就是一個裏面保存了訂閱者和訂閱方法信息的一個實體類,包括訂閱類中所有訂閱的事件類型和所有的訂閱方法等。
        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);
    }

findUsingInfo()方法會在當前要註冊的類以及其父類中查找訂閱事件的方法,最後再通過getMethodsAndRelease方法對findState做回收處理並返回訂閱方法的List集合。具體的查找過程在findUsingReflectionInSingleClass()方法,它主要通過反射查找訂閱事件的方法:

    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) {
            // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
            methods = findState.clazz.getMethods();
            findState.skipSuperClasses = true;
        }
        //循環遍歷當前類的方法,篩選出符合條件的
        for (Method method : methods) {
            int modifiers = method.getModifiers();
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                // 獲得當前方法所有參數的類型
                Class<?>[] parameterTypes = method.getParameterTypes();
                //保證只有一個事件參數
                if (parameterTypes.length == 1) {
                    //得到註解
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    // 如果當前方法使用了Subscribe註解
                    if (subscribeAnnotation != null) {
                        // 得到該參數的類型
                        Class<?> eventType = parameterTypes[0];
                        // checkAdd()方法用來判斷FindState的anyMethodByEventType map是否已經添加過以當前eventType爲key的鍵值對,沒添加過則返回true
                        if (findState.checkAdd(method, eventType)) {
                            // 得到Subscribe註解的threadMode屬性值,即線程模式
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            // 創建一個SubscriberMethod對象,並添加到subscriberMethods集合
                            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");
            }
        }
    }

查找的過程,就是對當前類中的方法進行遍歷,找到符合條件的方法(即添加了Subscribe註解),獲取相關屬性值,添加到SubscriberMethod對象,將符合條件的對象添加到subscriberMethods集合中,即保存到findState中。

1.3訂閱者的註冊過程

在查找完訂閱者的訂閱方法後,就要對其進行註冊。調用subscribe()方法進行註冊

    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        // 得到當前訂閱了事件的方法的參數類型
        Class<?> eventType = subscriberMethod.eventType;
        // Subscription類保存了要註冊的類對象以及當前的subscriberMethod
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        // subscriptionsByEventType是一個HashMap,保存了以eventType爲key,Subscription對象集合爲value的鍵值對
        // 先查找subscriptionsByEventType是否存在以當前eventType爲key的值
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions == null) {
            // 如果不存在,則創建一個subscriptions,並保存到subscriptionsByEventType
            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 = 0; i <= size; i++) {
            if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                // 添加上邊創建的newSubscription對象到subscriptions中
                subscriptions.add(i, newSubscription);
                break;
            }
        }
        // typesBySubscriber也是一個HashMap,保存了以當前要註冊類的對象爲key,註冊類中訂閱事件的方法的參數類型的集合爲value的鍵值對
        // 查找是否存在對應的參數類型集合
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            // 不存在則創建一個subscribedEvents,並保存到typesBySubscriber
            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>).
                // stickyEvents就是發送粘性事件時,保存了事件類型和對應事件
                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);
            }
        }
    }

subscribe()方法需要兩個參數,第一個是訂閱者,第二個是訂閱者方法,然後將符合條件的訂閱者信息保存到subscriptionsByEventType、typesBySubscriber兩個HashMap中,當我們在發送事件的時候要用到subscriptionsByEventType,完成事件的處理,當取消 EventBus 註冊的時候要用到typesBySubscriber、subscriptionsByEventType,完成相關資源的釋放。
處理粘性事件就是在 EventBus 註冊時,遍歷stickyEvents,如果當前要註冊的事件訂閱方法是粘性的,並且該方法接收的事件類型和stickyEvents中某個事件類型相同或者是其父類,則取出stickyEvents中對應事件類型的具體事件,通過checkPostStickyEventToSubscription()方法做進一步處理。

    private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
        if (stickyEvent != null) {
            // If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
            // --> Strange corner case, which we don't take care of here.
            postToSubscription(newSubscription, stickyEvent, isMainThread());
        }
    }

最終還是通過postToSubscription()方法完成粘性事件的處理,這就是粘性事件的整個處理流程。

註冊訂閱者總結:
(1).首先用register()方法註冊一個訂閱者
(2).獲取該訂閱者的所有訂閱的方法
(3).根據該訂閱者的所有訂閱的事件類型,將訂閱者存入到每個以 事件類型爲key 以所有訂閱者爲values的map集合中
(4).然後將訂閱事件添加到以訂閱者爲key 以訂閱者所有訂閱事件爲values的map集合中
(5).如果是訂閱了粘性事件的訂閱者,從粘性事件緩存區獲取之前發送過的粘性事件,響應這些粘性事件。

2.註銷訂閱者

下面看下注銷訂閱者

 EventBus.getDefault().unregister(this);

註銷訂閱者是調用unregister()方法

    public synchronized void unregister(Object subscriber) {
       // 得到當前註冊類對象 對應的訂閱事件方法的參數類型的集合
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
           // 遍歷參數類型集合,釋放之前緩存的當前類中的Subscription
            for (Class<?> eventType : subscribedTypes) {
                unsubscribeByEventType(subscriber, eventType);
            }
            // 刪除以subscriber爲key的鍵值對
            typesBySubscriber.remove(subscriber);
        } else {
            logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }
    private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
        // 得到當前參數類型對應的Subscription集合
        List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions != null) {
            int size = subscriptions.size();
            for (int i = 0; i < size; i++) {
                Subscription subscription = subscriptions.get(i);
                if (subscription.subscriber == subscriber) {
                    // 如果當前subscription對象對應的註冊類對象和要取消註冊的註冊類對象相同,則刪除當前subscription對象
                    subscription.active = false;
                    subscriptions.remove(i);
                    i--;
                    size--;
                }
            }
        }
    }

我們可以看到在unregister()方法中,釋放了typesBySubscriber、subscriptionsByEventType中緩存的資源。

註銷訂閱者總結:
(1).首先通過unregister方法拿到要取消的訂閱者
(2).得到該訂閱者的所有訂閱事件類型
(3).遍歷事件類型,根據每個事件類型獲取到所有的訂閱者集合,並從集合中刪除該訂閱者

3.發送及處理事件

當我們要發送普通事件,就需要用到post()方法,
當我們要發送粘性事件,就需要用到postSticky()方法

//發送普通事件
EventBus.getDefault().post("我要學習EventBus");
//發送粘性事件
EventBus.getDefault().postSticky(new MessageEvent("我要學習EventBus的粘性事件"));
3.1粘性事件 postSticky()方法
    public void postSticky(Object event) {
        synchronized (stickyEvents) {
            stickyEvents.put(event.getClass(), event);
        }
        // Should be posted after it is putted, in case the subscriber wants to remove immediately
        post(event);
    }

postSticky()方法主要做了兩件事,先將事件類型和對應事件保存到stickyEvents中,方便後續使用;然後執行post(event)繼續發送事件,這個post()方法就是之前發送普通消息的post()方法。所以,如果在發送粘性事件前,已經有了對應類型事件的訂閱者,不是非粘性的,依然可以接收到發送出的粘性事件。

3.2普通事件 post()方法
    public void post(Object event) {
       //currentPostingThreadState是一個PostingThreadState類型的ThreadLocal
       // PostingThreadState類保存了事件隊列和線程模式等信息
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        // 將要發送的事件添加到事件隊列
        eventQueue.add(event);
        // isPosting默認爲false
        if (!postingState.isPosting) {
            // 是否爲主線程
            postingState.isMainThread = isMainThread();
            postingState.isPosting = true;
            if (postingState.canceled) {
                throw new EventBusException("Internal error. Abort state was not reset");
            }
            try {
                while (!eventQueue.isEmpty()) {
                    // 發送單個事件,並且從事件隊列移除事件
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

post()方法先將發送的事件保存的事件隊列,然後通過循環出隊列,將事件交給postSingleEvent()方法處理:

   private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        // eventInheritance默認爲true,表示是否向上查找事件的父類
        if (eventInheritance) {
            // 查找當前事件類型的Class,連同當前事件類型的Class保存到集合
            List<Class<?>> 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);
        }
        //找不到事件異常處理
        if (!subscriptionFound) {
            if (logNoSubscriberMessages) {
                logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
            }
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                post(new NoSubscriberEvent(this, event));
            }
        }
    }

postSingleEvent()方法中,根據eventInheritance屬性,決定是否向上遍歷事件的父類型,然後用postSingleEventForEventType()方法進一步處理事件:

    private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
          // 獲取事件類型對應的Subscription集合
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        if (subscriptions != null && !subscriptions.isEmpty()) {
            for (Subscription subscription : subscriptions) {
                // 記錄事件
                postingState.event = event;
                // 記錄對應的subscription
                postingState.subscription = subscription;
                boolean aborted = false;
                try {
                    // 最終的事件處理
                    postToSubscription(subscription, event, postingState.isMainThread);
                    aborted = postingState.canceled;
                } finally {
                    //將postingState置爲初始狀態
                    postingState.event = null;
                    postingState.subscription = null;
                    postingState.canceled = false;
                }
                if (aborted) {
                    break;
                }
            }
            return true;
        }
        return false;
    }

遍歷發送的事件類型對應的Subscription集合,然後調用postToSubscription()方法處理事件。

    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 {
                    // 如果是在子線程發送事件,則將事件入隊列,通過Handler切換到主線程執行處理事件
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case MAIN_ORDERED:
                if (mainThreadPoster != null) {
                    // 無論在那個線程發送事件,都先將事件入隊列,然後通過 Handler 切換到主線程,依次處理事件。
                    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);
        }
    }

可以看到,postToSubscription()方法就是根據訂閱事件方法的線程模式、以及發送事件的線程來判斷如何處理事件。
發送事件總結:
(1).首先獲取當前線程的事件隊列
(2).將要發送的事件添加到事件隊列中
(3).根據發送事件類型獲取所有的訂閱者
(4).根據響應方法的執行模式,在相應線程通過反射執行訂閱者的訂閱方法

4.Subscribe註解

EventBus3.0 開始用Subscribe註解配置事件訂閱方法,不再使用方法名了,如:

 @Subscribe(threadMode = ThreadMode.MAIN)
    public void onShowEventMessage(MessageEvent messageEvent) {
        tvMessage.setText(messageEvent.getMessage());
    }

看下Subscribe註解的實現:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
    // 指定事件訂閱方法的線程模式,即在那個線程執行事件訂閱方法處理事件,默認爲POSTING
    ThreadMode threadMode() default ThreadMode.POSTING;
    // 是否支持粘性事件,默認爲false
    boolean sticky() default false;
    // 指定事件訂閱方法的優先級,默認爲0,如果多個事件訂閱方法可以接收相同事件的,則優先級高的先接收到事件
    int priority() default 0;
}

所以在使用Subscribe註解時可以根據需求指定threadMode、sticky、priority三個屬性。關於threadMode的屬性,文章開始已經介紹了。

關於EventBus的源碼就學習到這裏,如理解有誤,還望指正!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章