EventBus 線程切換原理


主要問題其實只有兩個,其一:如何判斷當前發送事件的線程是否是主線程;其二:如何在接收事件時指定線程並執行;
一個一個來看。

1.如何判斷是否在主線程發送

EventBus在初始化的時候會初始化一個MainThreadSupport對象,它會去獲取主線程的Looper對象並存起來。(當前最新版本如果不是Android環境MainThreadSupport會為空,非Android環境也就無需關注是否是UI主線程的問題了)
在發送消息的時候,EventBus會取出當前線程的Looper對象對象與主線程Looper對象做比較,如果相同,說明是在主線程發送消息,如果不同,說明是在子線程發送消息。以下是MainThreadSupport的代碼:

public interface MainThreadSupport { boolean isMainThread(); Poster createPoster(EventBus eventBus); class AndroidHandlerMainThreadSupport implements MainThreadSupport { private final Looper looper; public AndroidHandlerMainThreadSupport(Looper looper) { this.looper = looper; } @Override public boolean isMainThread() { return looper == Looper.myLooper(); } @Override public Poster createPoster(EventBus eventBus) { return new HandlerPoster(eventBus, looper, 10); } } } 
2.怎么在指定的線程執行訂閱者的方法

在找到訂閱者之后,判斷不同線程情況下執行訂閱方法的邏輯基本都在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 { mainThreadPoster.enqueue(subscription, event); } break; case MAIN_ORDERED: 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); } } 

以上代碼中,如何判斷是否是主線程上面已經說過了。
invokeSubscriber()這個方法其實就是拿到訂閱者的信息,直接執行訂閱方法了(通過反射獲取)。
subscription對象中有一個SubscriberMethod對象,而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; //省略若干代碼... } 

所以,從postToSubscription()方法可以看出,當threadMode是POSTING時,直接在當前線程執行,不做判斷,也就是從哪個線程發送,就從哪個線程執行訂閱方法;
我們這里主要來看threadMode為MAIN和BACKGROUND的情況:

在主線程執行

當threadMode為MAIN時,如果在主線程發送,直接在當前線程執行,沒有問題。

如果不在主線程發送,會有一個mainThreadPoster將包含訂閱者信息的對象加入隊列。這個mainThreadPoster其實是Handler的子類,它利用Handler的消息機制,發送消息並在主線程接收消息,獲取到訂閱者的信息后在主線程處理事件,從而實現在子線程發送消息,在主線程處理事件。
這里直接利用Hadler的現成機制,可謂簡明高效。

 

在子線程執行

當threadMode為BACKGROUND時,如果不在主線程發送,直接執行,沒有問題。

如果在主線程發送,這里有一個backgroundPoster將包含訂閱者信息的對象加入隊列。BackgroundPoster其實是Runnable的子類,在自己的run方法中不斷從隊列中取出訂閱者對象,執行訂閱方法。EventBus維護了一個線程池,BackgroundPoster會將自己丟到線程池中,執行自己的run方法,從而實現在在主線程發送事件,在子線程中執行訂閱方法。

以上,就是EventBus切換執行線程的主要流程。
其實並不難。
末尾附上一篇講解EventBus原理的簡練博文:戳這里


附:EventBus線程切換


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM