以前,對於activity和fragment之間的通信可以使用接口的方式,定義起來比較麻煩費事,偶然間發現可以使用EventBus,發現很好用,查看了一下官方說明:EventBus是針一款對Android的發佈/訂閱事件總線。它可以讓我們很輕鬆的實現在Android各個組件之間傳遞消息,並且代碼的可讀性更好,耦合度更低。但是在用的過程中總會出現一些問題,下面就將出現的問題詳細記錄一下,順便具體學習EventBus(GreenRobot)這個第三方開源庫,瞭解它內部的實現原理,以至於出了問題可以快速定位修復。
鏈接
官網: http://greenrobot.org/eventbus/documentation/
github: https://github.com/greenrobot/EventBus
分析環境
EventBus3.0。
EventBus3.0使用
對於EventBus的原理呢,可以參照一下官網的這張圖:
具體的使用方法可以看官網,很簡單,簡單羅列一下:
Add Gradle
compile 'org.greenrobot:eventbus:3.0.0'
Define events
public class MessageEvent {
public final String message;
public MessageEvent(String message) {
this.message = message;
}
}
Prepare subscribers
// This method will be called when a MessageEvent is posted (in the UI thread for Toast)
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
Post events
EventBus.getDefault().post(new MessageEvent("Hello everyone!"));
以上便是官網給出的簡單使用步驟,也不是很難,所以就不需要翻譯了。接下來我們針對使用過程中出現的問題來進行一步一步的深入探究。
踩坑
我們先來使用一個簡單的例子來總結。這個例子主要有三個activity,MainActivity、SecondActivity、ThirdActivity以及一個MessageEvent對象。我們在MainActivity、SecondActivity中分別註冊了MessageEvent事件,在ThirdActivity中post MessageEvent事件,這樣我們在MainActivity、SecondActivity中應該都能接受到該事件。下面是具體的代碼。
第一個MainActivity
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EventBus.getDefault().register(this);
Button btn = (Button) findViewById(R.id.button2);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, SecondActivity.class));
}
});
}
//接收事件
@Subscribe(threadMode = ThreadMode.MAIN)
public void fresh(MessageEvent messageEvent) {
M2Log.d("MessageEvent -----------------> MainActivity");
}
@Override
protected void onDestroy() {
super.onDestroy();
M2Log.d("MainActivity -----------------> onDestroy");
EventBus.getDefault().unregister(this);
}
}
第二個SecondActivity
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
EventBus.getDefault().register(this);
Button btn = (Button) findViewById(R.id.btn2);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(SecondActivity.this, ThirdActivity.class));
}
});
}
//接收事件
@Subscribe(threadMode = ThreadMode.MAIN)
public void fresh(MessageEvent messageEvent) {
M2Log.d("MessageEvent -----------------> SecondActivity");
}
@Override
protected void onDestroy() {
super.onDestroy();
M2Log.d("SecondActivity -----------------> onDestroy");
EventBus.getDefault().unregister(this);
}
}
第三個ThirdActivity
public class ThirdActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
Button btn = (Button) findViewById(R.id.btn3);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//發送消息
EventBus.getDefault().post(new MessageEvent(""));
finish();
}
});
}
}
打印輸出結果
很顯然,MainActivity和SecondActivity都接收到了MessageEvent事件。
細心的會發現,我們
EventBus.getDefault().register(this);
EventBus.getDefault().unregister(this);
註冊生命週期是放在onCreate()和onDestroy()中的,如果我們按照官網上來,放在onStart()和onStop()中,你就會發現,我們接收不到MessageEvent事件,可以驗證一下
@Override
protected void onStart() {
super.onStart();
M2Log.d("SecondActivity -----------------> onStart");
EventBus.getDefault().register(this);
}
@Override
protected void onStop() {
super.onStop();
M2Log.d("SecondActivity -----------------> onStop");
EventBus.getDefault().unregister(this);
}
結果是什麼都不會打印,所以我們一般會將註冊生命週期放到onCreate()和onDestroy()中去。
我們在開發過程中,你會發現有的時候會出現問題:
1、沒有註冊該事件
出現這種情況,大多數是沒有註冊該事件,什麼意思呢?就是下面的類似代碼沒有寫。
@Subscribe(threadMode = ThreadMode.MAIN)
public void fresh(MessageEvent messageEvent) {
M2Log.d("MessageEvent -----------------> MainActivity");
}
有的人寫了類似的註冊代碼,但還是會報這個錯誤,那就涉及到註冊的生命週期了。
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
2、多次註冊
這是我們在生命週期中註冊該事件時多次註冊造成的。解決方法很簡單,可以判斷一下
//沒有註冊時再進行註冊操作
if (!EventBus.getDefault().isRegistered(this)){
EventBus.getDefault().register(this);
}
粘性事件Sticky Events
粘性事件類似於粘性廣播,就是一次註冊永久使用。
如何使用呢?類似於前面的,只不過加了一個sticky = true,發送時採用postSticky而已
//發佈事件
EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!"));
在接收的時候添加一個sticky = true即可。
//註冊接收
@Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
public void fresh(MessageEvent messageEvent) {
M2Log.d("MessageEvent -----------------> SecondActivity");
}
@Override
protected void onStart() {
super.onStart();
M2Log.d("SecondActivity -----------------> onStart");
EventBus.getDefault().register(this);
}
@Override
protected void onStop() {
super.onStop();
M2Log.d("SecondActivity -----------------> onStop");
EventBus.getDefault().unregister(this);
}
我們前面出現過一個問題,那就是我們在onStart和onStop中註冊,接收不到EventMessage,通過粘性事件,就可以解決這個問題。不過當你使用粘性事件時你會發現,每次進入註冊該事件的activity中都會主動接收到該事件。
下面是我發送了一個粘性事件,我們在MainActivity 和 SecondActivity中會接收到該事件,我們退出APP後,再次進入,則又會接收到該事件。
清除粘性事件
MessageEvent stickyEvent = EventBus.getDefault().getStickyEvent(MessageEvent.class);
// Better check that an event was actually posted before
if(stickyEvent != null) {
// "Consume" the sticky event
EventBus.getDefault().removeStickyEvent(stickyEvent);
// Now do something with it
}
/**
* threadMode
* 表示方法在什麼線程執行 (Android更新UI只能在主線程, 所以如果需要操作UI, 需要設置ThreadMode.MainThread)
*
* sticky
* 表示是否是一個粘性事件 (如果你使用postSticky發送一個事件,那麼需要設置爲true才能接受到事件)
*
* priority
* 優先級 (如果有多個對象同時訂閱了相同的事件, 那麼優先級越高,會優先被調用.)
* */
@Subscribe(threadMode = ThreadMode.MainThread, sticky = true, priority = 100)
public void onEvent(MsgEvent event){
}
上面便是EventBus3.0的常規用法,我們在知道了常規用法後還不行,必須深入瞭解一下它的內部實現原理,否則到時候出了問題後不知道該如何解決,要知其然而之所以然。下面我們便來分析一下它的源碼。
源碼解析(EventBus3.0)
源碼解析部分主要從register、post、以及unregisger這三部分進行分析。
REGISTER分析
我們首先從註冊入手,先分析
EventBus.getDefault()
進入源碼:
static volatile EventBus defaultInstance;
/** Convenience singleton for apps using a process-wide EventBus instance. */
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
EventBus是單例模式存在的,使用了雙重判斷的方式,防止併發的問題,還能極大的提高效率。接着進入register(this)進行分析
//**EventBus.class ---> register**
/**
* Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
* are no longer interested in receiving events.
* <p/>
* Subscribers have event handling methods that must be annotated by {@link Subscribe}.
* The {@link Subscribe} annotation also allows configuration like {@link
* ThreadMode} and priority.
*/
public void register(Object subscriber) {
//反射調用,獲取訂閱者的類對象
Class<?> subscriberClass = subscriber.getClass();
//獲取訂閱者所有的訂閱方法以@Subscribe爲註解的一些public方法
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
//依次註冊這些訂閱方法
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
//對訂閱方法進行註冊
subscribe(subscriber, subscriberMethod);
}
}
}
其中有獲取訂閱方法的代碼
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
我們進入,分析一下,如何獲取訂閱方法。首先來看一下訂閱方法的類
//**SubscriberMethod.class**
/** Used internally by EventBus and generated subscriber indexes. */
public class SubscriberMethod {
final Method method; //方法
final ThreadMode threadMode; //執行線程
final Class<?> eventType; //接收的事件類型
final int priority; //優先級
final boolean sticky; //粘性事件
/** Used for efficient comparison */
String methodString;
//...省略部分代碼
}
SubscriberMethod是一個訂閱方法的實體類,裏面存儲了訂閱方法的一些基本信息,訂閱方法就是在類中以@Subscribe爲註解的一些public方法,注意是public方法否則會報錯,爲什麼是public方法我們下面會分析,給出原因,然後進入subscriberMethodFinder.findSubscriberMethods(subscriberClass),該代碼的作用主要是獲取當前類中所有的訂閱方法。我們來看看是如何獲取一個訂閱者所有的訂閱方法的:
//**SubscriberMethodFinder.class**
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//從緩存中獲取訂閱方法
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//是否忽略註解器生成的MyEventBusIndex類
if (ignoreGeneratedIndex) {
//利用反射來獲取訂閱類中的訂閱方法信息
subscriberMethods = findUsingReflection(subscriberClass);
} else {
//從註解器生成的MyEventBusIndex類中獲得訂閱類的訂閱方法信息
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;
}
}
對於獲取我們註冊的訂閱方法,首先就是通過緩存來獲取,如果沒有的話則通過以下兩種方式進行獲取:
EventBusAnnotationProcessor註解生成器在編譯期通過讀取@Subscribe()註解並解析,處理其中所包含的信息,然後生成java類來保存所有訂閱者關於訂閱的信息。
運行時使用反射來獲得這些訂閱者的信息
對於這兩種方式的分析,可以參考http://www.jianshu.com/p/f057c460c77e這裏面相關的內容。
對於第一種方法沒什麼好說的,我們來分析一下通過反射來獲取這些訂閱方法的方式,接下來分析通過反射獲取當前類中的訂閱方法
//**SubscriberMethodFinder.class**
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
//FindState其實就是一個裏面保存了訂閱者和訂閱方法信息的一個實體類,包括訂閱類中所有訂閱的事件類型和所有的訂閱方法等。
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
//獲取訂閱方法
findUsingReflectionInSingleClass(findState);
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
代碼不是太多,裏面涉及到一個類FindState,我們來看下,這是什麼東西,
static class FindState {
//訂閱方法
final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
//以event爲key,以method爲value
final Map<Class, Object> anyMethodByEventType = new HashMap<>();
//以method的名字生成一個methodKey爲key,該method的類(訂閱者)爲value
final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
final StringBuilder methodKeyBuilder = new StringBuilder(128);
Class<?> subscriberClass;
Class<?> clazz;
boolean skipSuperClasses;
SubscriberInfo subscriberInfo;
//...省略部分代碼
}
這個FindState其實就是一個裏面保存了訂閱者和訂閱方法信息的一個實體類,包括訂閱類中所有訂閱的事件類型和所有的訂閱方法等。我們接着分析下面的代碼。
findUsingReflectionInSingleClass(findState)
這行代碼便是獲取訂閱方法列表的重要代碼,我們進入查看一下:
//**SubscriberMethodFinder.class**
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();
//訂閱方法必須是must be public, non-static, and non-abstract
//獲取訂閱方法參數類型
Class<?>[] parameterTypes = method.getParameterTypes();
//註解方法必須只有一個參數
if (parameterTypes.length == 1) {
//獲取訂閱方法的註解
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
//該訂閱方法包含Subscribe註解
if (subscribeAnnotation != null) {
Class<?> eventType = parameterTypes[0];
//添加該註解方法
if (findState.checkAdd(method, eventType)) {
//該註解方法的線程模式
ThreadMode threadMode = subscribeAnnotation.threadMode();
//添加該註解方法
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");
}
}
}
可以看到,首先會得到訂閱類的class對象並通過反射獲取訂閱類中的所有方法信息,然後通過篩選獲取到訂閱方法集合。這裏面就解釋了爲什麼要以@Subscribe爲註解的方法,且必須是public類型,方法參數只有一個的原因。
//**Subscribe.java**
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.POSTING;
/**
* If true, delivers the most recent sticky event (posted with
* {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
*/
boolean sticky() default false;
/** Subscriber priority to influence the order of event delivery.
* Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
* others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
* delivery among subscribers with different {@link ThreadMode}s! */
int priority() default 0;
}
註解,分爲三種參數,
ThreadMode,方法執行的線程,POSTING(默認值)、MAIN、BACKGROUND、ASYNC
sticky,粘性時間,默認值false
priority,優先級,默認值0
該方法流程是:
拿到當前 class 的所有方法
過濾掉不是 public 和是 abstract、static、bridge、synthetic 的方法 過濾出方法參數只有一個的方法
過濾出被Subscribe註解修飾的方法
將 method 方法和 event 事件添加到 findState 中
將 EventBus 關心的 method 方法、event 事件、threadMode、priority、sticky 封裝成SubscriberMethod 對象添加到 findState.subscriberMethods 列表中
通過上面幾步,我們就可以獲得了所訂閱的方法,然後分別進行註冊這些訂閱方法。通過下面的代碼來執行:
//參數:1訂閱者 2訂閱方法
subscribe(subscriber, subscriberMethod);
接着分析這個註冊方法。
//**EventBus.java**
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//訂閱方法的參數類型,也是事件類型
Class<?> eventType = subscriberMethod.eventType;
//訂閱方法描述,實體類(當前類中的訂閱方法)
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//獲取當前類中的所有訂閱方法
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
//該訂閱方法還沒有進行註冊
if (subscriptions == null) {
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) {
subscriptions.add(i, newSubscription);
break;
}
}
//獲取訂閱者所有訂閱的事件類型
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
//將該事件類型添加到typesBySubscriber中
subscribedEvents.add(eventType);
//如果接收sticky事件,立即分發sticky事件
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>).
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);
}
}
}
這裏面涉及到一些對象,我們分別註釋一下:
//Subscription.java
//訂閱者信息
final class Subscription {
final Object subscriber;//訂閱者
final SubscriberMethod subscriberMethod;//訂閱方法
}
//subscriptionsByEventType
key訂閱方法類型 values 所有訂閱了該類型的訂閱者集合
Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
//typesBySubscriber
key訂閱者 values訂閱事件集合
Map<Object, List<Class<?>>> typesBySubscriber;
瞭解了這幾個對象,上面的代碼就很容易看懂了,
- 首先獲取訂閱方法的參數類型即訂閱事件類型
- 根據訂閱事件類型獲取該事件類型的所有訂閱者
- 將該訂閱者添加到該事件類型的訂閱者集合中即:subscriptionsByEventType
- 獲取訂閱者所有的訂閱事件類型
- 將該事件類型添加到該訂閱者的訂閱事件類型集中即:typesBySubscriber
事件POST分析
分析了註冊事件後,我們來分析一下分發事件post的流程,首先通過
EventBus.getDefault().post(new MessageEvent(""));
這行代碼進行事件消息的分發,我們進入到post中詳細瞭解一下這個流程。
/** Posts the given event to the event bus. */
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
if (!postingState.isPosting) {
postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
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;
}
}
}
代碼雖然不長,但是不大好理解,這裏面多了一些不常見的對象,我們來看下,首先對於第一行代碼:
PostingThreadState postingState = currentPostingThreadState.get();
這裏面的PostingThreadState是什麼意思呢?
/** For ThreadLocal, much faster to set (and get multiple values). */
final static class PostingThreadState {
//當前線程的事件隊列
final List<Object> eventQueue = new ArrayList<Object>();
//是否有事件正在分發
boolean isPosting;
//post的線程是否是主線程
boolean isMainThread;
Subscription subscription;
Object event;
boolean canceled;
}
PostingThreadState中包含了當前線程的事件隊列,就是當前線程所有分發的事件都保存在eventQueue事件隊列中以及訂閱者訂閱事件等信息,有了這些信息我們就可以從事件隊列中取出事件分發給對應的訂閱者。
我們接着分析,對於這個當前線程的事件隊列,我們是通過currentPostingThreadState.get();來得到的,對於這個currentPostingThreadState又是什麼呢?
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
ThreadLocal 是一個線程內部的數據存儲類,通過它可以在指定的線程中存儲數據,而這段數據是不會與其他線程共享的。可以看出currentPostingThreadState的實現是一個包含了PostingThreadState的ThreadLocal對象,這樣可以保證取到的都是自己線程對應的數據。
接着就通過postSingleEvent(eventQueue.remove(0), postingState);來對事件進行分發。
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
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 {
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它來執行,
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;
}
//將事件分發給對應的訂閱者
//將事件分發給對應的訂閱者
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 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);
}
}
代碼比較簡單,它們最終是通過發射調用來將事件分發給對應的訂閱者的:
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);
}
}
以上便是事件的分發過程,我們總結概括一下:
- 首先獲取當前線程的PostingThreadState對象從而獲取到當前線程的事件隊列
- 通過事件類型獲取到所有訂閱者集合
- 通過反射執行訂閱者中的訂閱方法
UNREGISTER分析
最後我們來分析一下取消訂閱的方法:
/** Unregisters the given subscriber from all event classes. */
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 {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
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--;
}
}
}
}
代碼很簡單,最後總結一下取消訂閱的流程。
總結
- 首先獲取訂閱者的所有訂閱事件
- 遍歷訂閱事件
- 根據訂閱事件獲取所有的訂閱了該事件的訂閱者集合
- 將該訂閱者移除
- 將步驟1中的集合中的訂閱者移除
以上便是EventBus所有的工作流程,我們來簡單說明一下:
register
- 首先用register()方法註冊一個訂閱者
- 獲取該訂閱者的所有訂閱的方法
- 根據該訂閱者的所有訂閱的事件類型,將訂閱者存入到每個以 事件類型爲key 以所有訂閱者爲values的map集合中
- 然後將訂閱事件添加到以訂閱者爲key 以訂閱者所有訂閱事件爲values的map集合中
- 如果是訂閱了粘滯事件的訂閱者,從粘滯事件緩存區獲取之前發送過的粘滯事件,響應這些粘滯事件。
post
- 首先獲取當前線程的事件隊列
- 將要發送的事件添加到事件隊列中
- 根據發送事件類型獲取所有的訂閱者
- 根據響應方法的執行模式,在相應線程通過反射執行訂閱者的訂閱方法
unregister
- 首先通過unregister方法拿到要取消的訂閱者
- 得到該訂閱者的所有訂閱事件類型
- 遍歷事件類型,根據每個事件類型獲取到所有的訂閱者集合,並從集合中刪除該訂閱者
- 將訂閱者從步驟2的集合中移除
關於作者
專注於 Android 開發多年,喜歡寫 blog 記錄總結學習經驗,blog 同步更新於本人的公衆號,歡迎大家關注,一起交流學習~