EventBus概述

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