Android消息傳遞之EventBus 3.0使用詳解

前言:

    前面兩篇不只學習了子線程與UI主線程之間的通訊方式,也學習瞭如何實現組件之間通訊,基於前面的知識咱們今天來分析一下EventBus是如何管理事件總線的,EventBus究竟是不是最佳方案?學習本篇知識以前建議先回顧一下前兩篇知識:Android消息傳遞之Handler消息機制(一)Android消息傳遞之組件間傳遞消息(二)html

  消息傳遞相關文章地址:java

EventBus產生需求背景:

    在作項目的時候每每須要應用程序內各組件間、組件與後臺線程間的通訊。好比耗時操做,等耗時操做完成後經過Handler或Broadcast將結果通知給UI,N個Activity之間須要經過Listener通訊,以前的實現方式咱們在Android消息傳遞之組件間傳遞消息(二)中已經介紹過了,其實這些均可以經過EventBus輕鬆實現,EventBus經過發佈/訂閱(publish/subscribe)方式來管理事件總線。其實EventBus的實現方式更加接近上篇文章的方式二,不一樣的是EventBus經過註解和反射機制 將訂閱者連同訂閱函數保存起來,而後在發送訂閱的時候 遍歷訂閱函數數組進行調用,其實從這方面就能夠EventBus執行效率多少會受到一點影響。android

EventBus介紹:

     EventBus出自greenrobot,和以前大名鼎鼎的GreenDao出自同一家。以前一直使用的是2.4版本,今天咱們將學習分析最新的Event 3.0,EventBus 3.0 最新的特性就是加入了註解,經過註解的方式 告知訂閱函數運行在哪一個線程中。git

     github地址:https://github.com/greenrobot/EventBusgithub

     官方文檔:http://greenrobot.org/eventbus/documentation數組

EventBus主要角色:

  •  Event 傳遞的事件對象
  •  Subscriber  事件的訂閱者 
  •  Publisher  事件的發佈者
  •  ThreadMode 定義函數在何種線程中執行

  官網給出的各類角色的協做圖app

EventBus配置:

  EventBus框架也是採用建造者模式設計的,能夠經過EventBusBuilder來設置一些配置信息,例如設置debug模式下要拋出異常框架

EventBus eventBus=EventBus.builder().throwSubscriberException(BuildConfig.DEBUG).build();

EventBus示例:

 以前作圖片社交App的時候,須要處理一個點贊數據的同步,好比在做品的詳情頁點贊 須要同時更新列表頁該做品的點贊數量,這裏仍是以此爲例。異步

 1.)build.gradle添加引用 
compile 'org.greenrobot:eventbus:3.0.0'
2.)定義一個事件類型
public class DataSynEvent {
    private int count;

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }
}
3.)訂閱/解除訂閱

 訂閱ide

EventBus.getDefault().register(this);//訂閱

 解除訂閱

EventBus.getDefault().unregister(this);//解除訂閱
4.)發佈事件
EventBus.getDefault().post(new DataSynEvent());
5.)訂閱事件處理
    @Subscribe(threadMode = ThreadMode.MAIN) //在ui線程執行
    public void onDataSynEvent(DataSynEvent event) {
        Log.e(TAG, "event---->" + event.getCount());
    }
ThreadMode總共四個:
  • NAIN UI主線程
  • BACKGROUND 後臺線程
  • POSTING 和發佈者處在同一個線程
  • ASYNC 異步線程
6.)訂閱事件的優先級

   事件的優先級相似廣播的優先級,優先級越高優先得到消息

  @Subscribe(threadMode = ThreadMode.MAIN,priority = 100) //在ui線程執行 優先級100
    public void onDataSynEvent(DataSynEvent event) {
        Log.e(TAG, "event---->" + event.getCount());
    }
7.)終止事件往下傳遞

 發送有序廣播能夠終止廣播的繼續往下傳遞,EventBus也實現了此功能

  EventBus.getDefault().cancelEventDelivery(event) ;//優先級高的訂閱者能夠終止事件往下傳遞
8.)處理代碼混淆
-keepattributes *Annotation*
-keepclassmembers class ** {
    @org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }

# Only required if you use AsyncExecutor
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
    <init>(java.lang.Throwable);
}

EventBus黏性事件

   EventBus除了普通事件也支持粘性事件,這個有點相似廣播分類中的粘性廣播。自己粘性廣播用的就比較少,爲了方便理解成訂閱在發佈事件以後,但一樣能夠收到事件。訂閱/解除訂閱和普通事件同樣,可是處理訂閱函數有所不一樣,須要註解中添加sticky = true

  @Subscribe(threadMode = ThreadMode.MAIN,sticky = true) //在ui線程執行
    public void onDataSynEvent(DataSynEvent event) {
        Log.e(TAG, "event---->" + event.getCount());
    }

發送粘性事件

  EventBus.getDefault().postSticky(new DataSynEvent());

對於粘性廣播咱們都比較清楚屬於常駐廣播,對於EventBus粘性事件也相似,咱們若是再也不須要該粘性事件咱們能夠移除

  EventBus.getDefault().removeStickyEvent(new DataSynEvent());

或者調用移除全部粘性事件

  EventBus.getDefault().removeAllStickyEvents();

EventBus processor使用:

   EventBus提供了一個EventBusAnnotationProcessor註解處理器來在編譯期經過讀取@Subscribe()註解並解析,
處理其中所包含的信息,而後生成java類來保存全部訂閱者關於訂閱的信息,這樣就比在運行時使用反射來得到這些訂閱者的
信息速度要快.

 1.)具體使用:在build.gradle中添加以下配置
buildscript {
    dependencies {
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }
}
apply plugin: 'com.neenbedankt.android-apt'

dependencies {
    compile 'org.greenrobot:eventbus:3.0.0'
    apt 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}
apt {
    arguments {
        eventBusIndex "com.whoislcj.eventbus.MyEventBusIndex"
    }
}
2.)使用索引

此時編譯一次,自動生成生成索引類。在\build\generated\source\apt\PakageName\下看到經過註解分析生成的索引類,這樣咱們即可以在初始化EventBus時應用咱們生成的索引了。

自動生成的代碼

/** This class is generated by EventBus, do not edit. */
public class MyEventBusIndex implements SubscriberInfoIndex {
    private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;

    static {
        SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();

        putIndex(new SimpleSubscriberInfo(com.whoislcj.testhttp.MainActivity.class, true, new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("onDataSynEvent", com.whoislcj.testhttp.eventBus.DataSynEvent.class,
                    ThreadMode.MAIN, 100, false),
            new SubscriberMethodInfo("onDataSynEvent1", com.whoislcj.testhttp.eventBus.TestEvent.class, ThreadMode.MAIN,
                    0, true),
        }));

    }

    private static void putIndex(SubscriberInfo info) {
        SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
    }

    @Override
    public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
        SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
        if (info != null) {
            return info;
        } else {
            return null;
        }
    }
}

添加索引到EventBus默認的單例中

EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
3.)對比添加先後註冊效率對比
分別EventBus.getDefault().register(this);

  添加以前:先後用了9毫秒

  

  添加以後:先後用了2毫秒 

 EventBus優缺點:

   優勢:簡化組件之間的通訊方式,實現解耦讓業務代碼更加簡潔,能夠動態設置事件處理線程以及優先級

   缺點:目前發現惟一的缺點就是相似以前策略模式同樣的詬病,每一個事件都必須自定義一個事件類,形成事件類太多,無形中加大了維護成本

EventBus 3.0 與2.x的區別

 1.)代碼更加簡潔

   EventBus 2.x 必須定義以onEvent開頭的幾個方法,代碼中語境比較突兀,且有可能會致使拼寫錯誤,例如數據同步事件

    public void onEvent(DataSynEvent event) {
        //事件在哪一個線程發佈出來的,onEvent就會在這個線程中運行, 同 @Subscribe(threadMode = ThreadMode.POSTING)
    }

    public void onEventMainThread(DataSynEvent event) {
        // 不論事件是在哪一個線程中發佈出來的,onEventMainThread都會在UI線程中執行,接收事件就會在UI線程中運行,同 @Subscribe(threadMode = ThreadMode.MAIN)
    }

    public void onEventBackgroundThread(DataSynEvent event) {
        //那麼若是事件是在UI線程中發佈出來的,那麼onEventBackground就會在子線程中運行,若是事件原本就是子線程中發佈出來的,那麼onEventBackground函數直接在該子線程中執行,同 @Subscribe(threadMode = ThreadMode.BACKGROUND)
    }


    public void onEventAsync(DataSynEvent event) {
        //使用這個函數做爲訂閱函數,那麼不管事件在哪一個線程發佈,都會建立新的子線程在執行onEventAsync,同 @Subscribe(threadMode = ThreadMode.ASYNC)
    }

EventBus  3.0 函數名字再也不受到權限,並且能夠在一個函數中體現出在哪一個線程執行,而且可指定接收事件的優先級

 /**
     * 普通事件
     * @param event
     */
    @Subscribe(threadMode = ThreadMode.MAIN, priority = 100) 
    public void onDataSynEvent(DataSynEvent event) {
       
    }

    /**
     * 粘性事件
     * @param event
     */
    @Subscribe(threadMode = ThreadMode.MAIN, priority = 100, sticky = true) 
    public void onDataSynEvent(DataSynEvent event) {
       
    }

EventBus 2.x 註冊方式也比較繁瑣

  public void register(Object subscriber) {
  register(subscriber, false, 0);
  }
  
  public void register(Object subscriber, int priority) {
  register(subscriber, false, priority);
  }
  
  public void registerSticky(Object subscriber) {
  register(subscriber, true, 0);
  }
  
  public void registerSticky(Object subscriber, int priority) {
  register(subscriber, true, priority);
  }
  private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
  ...
  }

EventBus  3.0 註冊方式只有一個

    public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

以上仍是在一個訂閱者僅僅訂閱一個事件的狀況下,若是訂閱多個事件,可想而知EventBus 2.x勢必致使訂閱者要寫大量的多態函數,若是訂閱多種類型事件,好比普通事件和粘性事件並存,估計要同時調用register,registerSticky兩個函數。

2.)性能更優

  EventBus 2.x 是採用反射的方式對整個註冊的類的全部方法進行掃描來完成註冊,固然會有性能上的影響。EventBus  3.0中EventBus提供了EventBusAnnotationProcessor註解處理器來在編譯期經過讀取@Subscribe()註解並解析、處理其中所包含的信息,而後生成java類來保存全部訂閱者關於訂閱的信息,這樣就比在運行時使用反射來得到這些訂閱者的信息速度要快

小結:

     EventBus 3.0的使用基本上總結完了,以前一直擔憂EventBus經過註解或者反射會影響太多性能,隨着3.0的發佈這部分影響已經很小了。

 知識擴展:

     有關EventBus實現原理已經有大神作了很是細緻的解說,這裏就不作具體分析了,參考博客地址:http://www.jianshu.com/p/f057c460c77e
相關文章
相關標籤/搜索