EventBus概述
一、定義
EventBus 主要用於Android組件之間、組件與後臺線程之間通信,是一個事件發佈/訂閱的輕量級開源庫
GitHub 百科
二、使用
- 1、定義事件
/**Events are POJO (plain old Java object) without any specific requirements*/
public class MessageEvent {
public final String message;
public MessageEvent(String message) {
this.message = message;
}
}
- 2、訂閱者註冊並訂閱事件
public class TestActivity extends Activity {
//訂閱者註冊
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
//訂閱者訂閱事件
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
//處理事件、MessageEvent事件發佈後該方法將被調用
}
}
- 3、發佈事件
/**
* 代碼的任意位置發佈MessageEvent事件後,當前所有訂閱MessageEvent事件的訂閱者都將收到該事件
* Post an event from any part of your code.
* All currently registered subscribers matching the event type will receive it.
*/
EventBus.getDefault().post(new MessageEvent("Hello world!"));
三、實現
- 1、單例EventBus
/** Convenience singleton for apps using a process-wide EventBus instance. */
public static EventBus getDefault() {
EventBus instance = defaultInstance;
if (instance == null) {
synchronized (EventBus.class) {
instance = EventBus.defaultInstance;
if (instance == null) {
instance = EventBus.defaultInstance = new EventBus();
}
}
}
return instance;
}
- 2、註冊
/**註冊時,通過反射找到訂閱者中訂閱事件的方法 */
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
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);
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");
}
}
}
/**將事件類型-訂閱者及其方法分別作爲:key-value放到Map中*/
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
- 3、發佈事件
/**根據事件類型,從上面"註冊"時的提到Map中找到訂閱者及其方法*/
private boolean postSingleEventForEventType() {
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass);
}
......
}
/**根據訂閱者方法的註解參數threadMode {@Subscribe(threadMode = ThreadMode.MAIN)},在不同線程調用訂閱者方法*/
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;
}
}
threadMode,線程模式,主要有以下類型:
/**在哪個線程發佈的事件,就在哪個線程調用訂閱者訂閱事件方法,默認模式*/
ThreadMode.POSTING
/**若在主線程發佈的事件,則直接在主線程調用訂閱者方法;若是在子線程發佈的事件,事件會被先加入隊列,
利用Handler切到主線程後,在主線程依次調用訂閱者方法*/
ThreadMode.MAIN
/**在任意線程發佈的事件,事件都先被加入隊列,利用Handler切到主線程後,在主線程依次調用訂閱者方法*/
ThreadMode.MAIN_ORDERED
/**若在子線程發佈的事件,則直接在子線程調用訂閱者方法;若是在主線程發佈的事件,事件會被加入隊列,然後在單
個後臺線程中依次調用訂閱者方法*/
ThreadMode.BACKGROUND
/**在任意線程發佈的事件,事件都先被加入隊列,然後分別在新線程中調用訂閱者方法*/
ThreadMode.ASYNC
四、優缺點
- 1、優點
項目開發時,App的各組件間、組件與後臺線程間需要進行通信(例:在子線程中進行請求數據,當數據請求完畢通過
Handler,Broadcast等方式通知UI).當項目變複雜時會出現代碼量大耦合度高的問題,EventBus可以代替這些傳統的通信
方式,簡化組件之間的通信、將事件發送者和接收者分離(github官方觀點) - 2、缺點
反射的可能會影響一些效率、事件可能會糾纏(例:訂閱者方法中直接、間接拋出其他事件),業務量大時會出現
“Events were posted everywhere for everything”,難調試 [1] [2]
五、參考
https://github.com/greenrobot/EventBus
http://greenrobot.org/eventbus/documentation/
https://baike.baidu.com/item/EventBus/20461274
https://endlesswhileloop.com/blog/2015/06/11/stop-using-event-buses/
https://medium.com/@gmirchev90/its-21st-century-stop-using-eventbus-3ff5d9c6a00f