前言:作為一名Android開發(fā)人員葡缰,EventBus這個開源庫想必大家在日常的開發(fā)工作中都有用到吧。有關(guān)EventBus的介紹,在這里就略過了泛释。如果你還不了解的話滤愕,參照EventBus官方文檔。記得我第一次接觸到EventBus的時候怜校,就被它的強(qiáng)大深深折服了间影,很好奇它內(nèi)部是怎么工作的。這兩天翻看了下源碼茄茁,做下學(xué)習(xí)筆記魂贬,希望也能幫助到大家。在這里我選取的EventBus版本為3.1.1裙顽,也就是當(dāng)前的最新版本。
我們知道键科,在EventBus中勋颖,如果我們想要訂閱事件的話酸钦,首先我們必須要注冊成為訂閱者:
protected void onResume() {
super.onResume();
EventBus.getDefault().register(this);
}
我們點進(jìn)去EventBus的getDefault()方法看一下:
public EventBus() {
this(DEFAULT_BUILDER); //EventBus對象在創(chuàng)建的時候使用了建造者模式
}
接著跟進(jìn)去:
EventBus(EventBusBuilder builder) {
logger = builder.getLogger();
//1.subscriptionsByEventType 是一個hashmap徒恋,以事件類型為key入挣,以訂閱者事件集合為value径筏,代表的是事件類型與其訂閱者事件之間的對應(yīng)關(guān)系。
//每個事件類型恢氯,可能對應(yīng)多個訂閱者事件勋磕,所以value值為訂閱者事件集合挂滓。
subscriptionsByEventType = new HashMap<>();
//2.typesBySubscriber 是一個hashmap,以訂閱者為key吓揪,以事件類型集合為value团秽,代表的是訂閱者與其下事件類型之間的對應(yīng)關(guān)系习勤。
//每個訂閱者可能會對應(yīng)多個事件類型,所以value值為事件類型集合予颤。
typesBySubscriber = new HashMap<>();
//3.粘性事件
stickyEvents = new ConcurrentHashMap<>();
mainThreadSupport = builder.getMainThreadSupport();
//4.mainThreadPoster 切換至主線程時使用
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
//5.backgroundPoster 切換至后臺線程時使用
backgroundPoster = new BackgroundPoster(this);
//6.獨立開辟新線程時使用
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;
}
EventBus的主要部分就是我在上述代碼中標(biāo)注的六點。大家可以先理解下驳庭。
EventBus的getDefault方法就被我們分析完了饲常,我們了解了EventBus對象的創(chuàng)建過程。接下來就開始解刨register方法了霹娄。我們照例跟進(jìn)去:
public void register(Object subscriber) {
// 1
Class<?> subscriberClass = subscriber.getClass();
// 2
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
// 3
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
我們通常會在Activity或者Fragment的onResume方法中完成訂閱操作踩晶,將當(dāng)前Activity對象或者Fragment對象作為參數(shù)傳入渡蜻。那么這個時候當(dāng)前Activity對象或者Fragment對象就作為了訂閱者。
首先在 1 處獲得當(dāng)前訂閱者對應(yīng)的Class對象学密,然后調(diào)用了SubscriberMethodFinder的findSubscriberMethods方法腻暮,將Class對象作為參數(shù)傳入哭靖,來獲取到當(dāng)前訂閱者對應(yīng)的所有訂閱方法。最后將訂閱方法集合進(jìn)行遍歷操作铺坞,分別對每個訂閱方法完成“訂閱”康震。
接下來我們跟進(jìn)去SubscriberMethodFinder(訂閱者方法找尋器)的findSubscriberMethods方法去看一下,看下它是怎么獲取到當(dāng)前訂閱者對應(yīng)的所有訂閱方法的绘梦。
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
// 1
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
// 2
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}
// 3
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;
}
}
我先簡單介紹下,METHOD_CACHE為一個HashMap榄棵,以訂閱者對應(yīng)的Class對象為key瘪弓,以訂閱方法集合為value腺怯,用作訂閱方法的緩存虑乖。在 1 處首先從METHOD_CACHE中獲取當(dāng)前訂閱者對應(yīng)的訂閱方法集合决左,如果返回的訂閱方法列表不為null,則直接返回訂閱方法列表继找。因為我們是首次訂閱婴渡,所以subscriberMethods 為null边臼,程序接著往下走。 2 處的ignoreGeneratedIndex在EventBus實現(xiàn)中默認(rèn)為false臼予,會走到else語句粘拾,通過findUsingInfo方法來獲取當(dāng)前訂閱者對應(yīng)的訂閱方法集合缰雇。 3 處會判斷當(dāng)前訂閱者對應(yīng)的訂閱方法集合是否為空盯串,如果為空体捏,會直接拋出異常几缭。如果訂閱方法集合不為空年栓,則將訂閱者Class對象與對應(yīng)的訂閱方法集合存儲到METHOD_CACHE緩存中某抓。
下面我們針對 3 處拋出的異常做個驗證。怎么樣才會讓EventBus拋出這個異常呢备禀?根據(jù)我們剛才的分析曲尸,那就是使當(dāng)前訂閱者對應(yīng)的訂閱方法集合為空另患。怎么著才會使它為空呢?很簡單为严,我們在一個Activity中只參與訂閱而沒有對應(yīng)的事件接收方法:
在這里我以MainActivity為例,只在它的onResume方法中完成了訂閱操作应民,而在MainActivity沒有任何事件接收方法繁仁。(當(dāng)然你在onDestroy方法中取不取消訂閱都沒有影響)MainActivity代碼如下:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onResume() {
super.onResume();
EventBus.getDefault().register(this);
}
}
這個時候我們運(yùn)行下我們的程序:
Caused by: org.greenrobot.eventbus.EventBusException: Subscriber class com.example.administrator.eventbustest.MainActivity and its super classes have no public methods with the @Subscribe annotation
at org.greenrobot.eventbus.SubscriberMethodFinder.findSubscriberMethods(SubscriberMethodFinder.java:67)
at org.greenrobot.eventbus.EventBus.register(EventBus.java:140)
at com.example.administrator.eventbustest.MainActivity.onResume(MainActivity.java:19)
at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1263)
at android.app.Activity.performResume(Activity.java:6160)
at android.app.Activity.performResume(Activity.java:6355)
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3053)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3102)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2463)
果然如此稚矿,app部署到手機(jī)上直接一閃而退苛败。對比下異常信息,你會發(fā)現(xiàn)正是我們在 3 處拋出的異常哈哈辆它,異常信息提示為:訂閱者類MainActivity及它的所有父類都沒有使用@Subscribe注解標(biāo)注的public方法甸祭。我們通過這個小實驗發(fā)散開來淋叶,無論是Android的FrameWork層還是第三方開源框架煞檩,是不是我們在了解了其內(nèi)部源碼后會對我們定位一些異常信息有幫助呢斟湃?對!絕對是有幫助的坛缕!
咳咳毙沾,我們接著我們之前的思路左胞,定位到 2 處遍烦,具體怎么通過findUsingInfo方法來獲取當(dāng)前訂閱者對應(yīng)的訂閱方法集合的呢服猪?我們跟進(jìn)去看一下:
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
//1. 通過調(diào)用prepareFindState方法來獲取FindState對象
FindState findState = prepareFindState();
//2. 對findstate對象進(jìn)行初始化操作
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 {
//3. 通過反射等操作獲取到訂閱方法
// 將訂閱方法添加到FindState的subscriberMethods集合中
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
1 處慨丐,通過調(diào)用prepareFindState方法來獲取FindState對象房揭。是怎么獲取的恬砂?FindState對象又是用來做什么的呢?在這里我簡單說下狱掂,F(xiàn)indState對象就是用來臨時存儲當(dāng)前查找狀態(tài)的趋惨。獲取操作代碼:
// private static final int POOL_SIZE = 4;
// private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];
private FindState prepareFindState() {
synchronized (FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
FindState state = FIND_STATE_POOL[i];
if (state != null) {
FIND_STATE_POOL[i] = null;
return state;
}
}
}
return new FindState();
}
在這里我簡單說下,F(xiàn)IND_STATE_POOL它就是一個FindState類型數(shù)組兆沙,數(shù)組大小為4葛圃,用來作為FindState對象的緩存昏鹃。
代碼中從FIND_STATE_POOL的腳標(biāo)為0處開始遍歷操作阅嘶,如果返回的FindState對象不為空抡蛙,就直接將FindState對象return掉,否則調(diào)用new FindState()進(jìn)行新對象創(chuàng)建操作熊昌。
接著看下 2 處:
void initForSubscriber(Class<?> subscriberClass) {
this.subscriberClass = clazz = subscriberClass;
skipSuperClasses = false;
subscriberInfo = null;
}
2 處對findstate對象進(jìn)行了初始化操作,將訂閱者Class對象賦值給成員變量subscriberClass和clazz昂利。
接著讓我們來到 3 處,看下具體怎么操作的:
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// 1. 我們剛才對findState.clazz進(jìn)行了賦值操作窝撵,也就是我們的訂閱者Class對象
//通過反射獲取到該訂閱者中所有聲明的方法
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;
}
//遍歷methods方法數(shù)組
for (Method method : methods) {
// 獲取方法修飾符并判斷是不是PUBLIC修飾或者默認(rèn)修飾符
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
//獲得方法的參數(shù)類型寒砖,判斷參數(shù)個數(shù)是否為1
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
//獲得方法Subscribe類型注解并判斷是否為null
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
//獲取參數(shù)類型哩都,也就是EventBus訂閱方法事件類型
Class<?> eventType = parameterTypes[0];
if (findState.checkAdd(method, eventType)) {
//獲取到訂閱方法所要運(yùn)行的線程環(huán)境魁兼,與EventBus使用時指定的線程方式對應(yīng)。
ThreadMode threadMode = subscribeAnnotation.threadMode();
//創(chuàng)建SubscriberMethod對象漠嵌,并添加到subscriberMethods訂閱者方法集合中
//SubscriberMethod中封裝了訂閱方法咐汞、事件類型盖呼、線程環(huán)境以及優(yōu)先級等信息
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");
}
}
}
我們回到findUsingInfo方法的最后一句return getMethodsAndRelease(findState);
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
findState.recycle();
synchronized (FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
if (FIND_STATE_POOL[i] == null) {
FIND_STATE_POOL[i] = findState;
break;
}
}
}
return subscriberMethods;
}
getMethodsAndRelease方法所做的操作就是將剛才新創(chuàng)建的findState對象中存儲的信息全部清空掉,然后添加到FIND_STATE_POOL緩存數(shù)組中化撕。最后將訂閱者方法集合返回。
終于又折回到最初的register方法中了:
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
接下來我們跟進(jìn)去subscribe方法憾朴,看下是怎么對每個訂閱方法完成“訂閱”的做祝。
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//1.獲取到訂閱方法對應(yīng)的事件類型
Class<?> eventType = subscriberMethod.eventType;
//2.對當(dāng)前訂閱者和訂閱方法進(jìn)一步封裝翻诉,包裝成Subscription對象(訂閱者事件)
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//3.調(diào)用subscriptionsByEventType 的get方法,將事件類型作為key傳入夜焦,獲取到該事件類型對應(yīng)的訂閱者事件集合
//關(guān)于 subscriptionsByEventType 在EventBus的構(gòu)造方法中已經(jīng)講述過了抹镊,忘記的話可以向上翻看一下
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
//4. 如果該事件類型對應(yīng)的訂閱者事件集合為null儡炼,則會新創(chuàng)建一個訂閱者事件集合唬党,并以該事件類型為key蓝纲,以新創(chuàng)建的訂閱者事件集合為value,添加到subscriptionsByEventType中嗅辣。
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
//當(dāng)我們在一個Activity或者Fragment中,有相同的訂閱方法時(與方法名稱無關(guān)),會拋出該異常阅签。
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
//5. 將新創(chuàng)建的訂閱者事件按照優(yōu)先級添加到訂閱者事件集合中
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
//6. 通過typesBySubscriber的get方法碎连,獲取到當(dāng)前訂閱者所對應(yīng)的訂閱事件類型集合
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
//7. 將該事件類型添加到事件類型集合中
subscribedEvents.add(eventType);
//8.判斷是否為粘性事件
if (subscriberMethod.sticky) {
// eventInheritance默認(rèn)為true,只是起到優(yōu)化作用口四,對后續(xù)調(diào)用沒有影響
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>).
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();
//9. 如果是粘性事件稚照,最終會調(diào)用到此方法
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
下面我們簡單看下 9 處的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());
}
}
可以看到checkPostStickyEventToSubscription內(nèi)部最終又是調(diào)用postToSubscription方法實現(xiàn)的。我們使用EventBus的post方法發(fā)送普通事件,最終也是調(diào)用到postToSubscription方法來處理的治筒∶暾可以簡單說粘性事件就是模擬普通事件的post方法來實現(xiàn)的。這也就解釋了粘性事件在發(fā)送之后注冊仍舊可以收到的原因了。
以上就是EventBus的register方法全部實現(xiàn)過程。
下面我們接著看下EventBus的取消訂閱方法约啊,也就是EventBus.getDefault().unregister(this);
我們點進(jìn)去unregister方法里面看下:
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
unregister的方法實現(xiàn)很簡單榔组。首先會調(diào)用typesBySubscriber的get方法,獲取到當(dāng)前訂閱者對應(yīng)的事件類型集合。然后對該事件類型集合進(jìn)行判斷医增,如果集合為null忽刽,則直接打印出警告信息扑眉,提示我們“訂閱者”在此之前并沒有訂閱過。如果集合不為null呆抑,則會進(jìn)行兩部操作:第一步品洛,遍歷當(dāng)前訂閱者對應(yīng)的事件類型集合查邢,調(diào)用subscriptionsByEventType的get方法蔗崎,獲取到每個事件類型對應(yīng)的訂閱者事件集合,然后再對每個事件類型對應(yīng)的訂閱者事件集合進(jìn)行遍歷操作扰藕,從訂閱者事件集合中將當(dāng)前訂閱者的訂閱事件移除掉缓苛。第二步,將當(dāng)前訂閱者從typesBySubscriber中移除掉邓深。
下面我貼下unsubscribeByEventType方法的源碼未桥,相信你一看就會理解了。
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
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.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
就這樣芥备,EventBus取消訂閱的方法也就分析完畢了冬耿,有沒有很簡單哈哈。
接下來終于到了我們的post發(fā)送事件的方法了:
EventBus.getDefault().post(new MessageEvent("hello"));我們點進(jìn)去post方法去看下:
public void post(Object event) {
//private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>()
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
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;
}
}
}
首先從currentPostingThreadState中獲取到PostingThreadState這個對象萌壳,currentPostingThreadState是個什么東西亦镶??袱瓮?我在代碼中已經(jīng)標(biāo)注過了缤骨,它就是一個ThreadLocal,關(guān)于ThreadLocal懂讯,相信看過Handler源碼的同志都會熟悉荷憋,對的,簡單理解下褐望,ThreadLocal保證了線程間數(shù)據(jù)的獨立性。然后將event事件添加到事件隊列中串前,接著進(jìn)入到if語句里面瘫里,標(biāo)記當(dāng)前線程是不是主線程。接著進(jìn)入while循環(huán)荡碾,不斷從事件隊列頭部取出事件谨读,調(diào)用postSingleEvent方法,逐個發(fā)送事件坛吁。我們跟進(jìn)去看下:
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
//1. 獲取到當(dāng)前事件對應(yīng)的事件Class
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
//2.無論eventInheritance的值為true 或者 false劳殖,最終都會調(diào)用到postSingleEventForEventType方法铐尚。
if (eventInheritance) {
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 {
//3. 調(diào)用postSingleEventForEventType方法。
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));
}
}
}
我們跟進(jìn)去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;
}
方法中首先會調(diào)用subscriptionsByEventType的get方法哆姻,獲取到當(dāng)前事件類型對應(yīng)的訂閱者事件集合宣增。接下來對當(dāng)前事件類型對應(yīng)的訂閱者事件集合進(jìn)行判斷,最終又會調(diào)用到postToSubscription方法中矛缨。postToSubscription方法爹脾??箕昭?有沒有一種似曾相識的感覺灵妨?對的,就是我們之前分析的粘性事件最后調(diào)用到的方法哈哈落竹。
我們點進(jìn)去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);
}
}
在方法中首先對訂閱者事件標(biāo)注的線程進(jìn)行判斷泌霍,這里我們就拿MAIN線程來舉例。如果訂閱者事件標(biāo)注的線程為MAIN述召,就表明我們的訂閱者事件方法體想要在UI線程中執(zhí)行朱转,這個時候會判斷當(dāng)前線程是不是主線程,如果是主線程就直接調(diào)用invokeSubscriber方法進(jìn)行操作桨武,如果不是主線程肋拔,則通過mainThreadPoster進(jìn)行線程切換咯。
我們先看下當(dāng)前線程是主線程的情況呀酸,也就是會直接調(diào)用到invokeSubscriber方法凉蜂。我們跟進(jìn)去看下:
void invokeSubscriber(Subscription subscription, Object event) {
try {
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
} catch (InvocationTargetException e) {
handleSubscriberException(subscription, event, e.getCause());
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unexpected exception", e);
}
}
what? 性誉? 窿吩?最終竟然還是通過反射來實現(xiàn)方法調(diào)用的哈哈哈哈!
如果當(dāng)前線程不是主線程的時候错览,又進(jìn)行怎么處理的呢纫雁?這里我們看到會調(diào)用mainThreadPoster的enqueue方法來將訂閱者事件和event對象進(jìn)行入隊操作。話說mainThreadPoster是個什么鬼倾哺?轧邪??
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);
}
}
}
這里我只粘貼了部分代碼羞海,我簡單說下忌愚,mainThreadPoster是由MainThreadSupport來創(chuàng)建的,由上述代碼可知却邓,MainThreadSupport是個接口硕糊,它的的實現(xiàn)類為AndroidHandlerMainThreadSupport。mainThreadPoster對象也就是HandlerPoster實例對象。最終也就是調(diào)用到HandlerPoster的enqueue方法來將訂閱者事件和event對象進(jìn)行入隊操作简十。在這里還要提一點檬某,AndroidHandlerMainThreadSupport中的looper對象為主線程looper。
我們接著看下HandlerPoster的enqueue方法:
//1. public class HandlerPoster extends Handler implements Poster {
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
//2. private final PendingPostQueue queue = new PendingPostQueue();
queue.enqueue(pendingPost);
if (!handlerActive) {
handlerActive = true;
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
}
}
}
可以看到最終又是調(diào)用到了PendingPostQueue的enqueue方法進(jìn)行入隊操作螟蝙。在這里我們直接看下HandlerPoster的handleMessage方法恢恼,該方法運(yùn)行在主線程。
@Override
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
while (true) {
PendingPost pendingPost = queue.poll();
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
handlerActive = false;
return;
}
}
}
//1.調(diào)用eventBus的invokeSubscriber方法胶逢。
eventBus.invokeSubscriber(pendingPost);
long timeInMethod = SystemClock.uptimeMillis() - started;
if (timeInMethod >= maxMillisInsideHandleMessage) {
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
rescheduled = true;
return;
}
}
} finally {
handlerActive = rescheduled;
}
}
可以看到厅瞎,最終調(diào)用到了eventBus的invokeSubscriber方法,而invokeSubscriber又調(diào)用到invokeSubscriber方法初坠。同樣還是通過反射來實現(xiàn)方法調(diào)用和簸,只不過多了一個線程切換而已。
關(guān)于EventBus的源碼就解析到這里了碟刺,筆者能力有限锁保,有什么表達(dá)不正確的,還望各位老哥指出半沽,如果有幫助到你們爽柒,還望點顆小心心支持下。