前面兩篇不只學習了子線程與UI主線程之間的通訊方式,也學習瞭如何實現組件之間通訊,基於前面的知識咱們今天來分析一下EventBus是如何管理事件總線的,EventBus究竟是不是最佳方案?學習本篇知識以前建議先回顧一下前兩篇知識:Android消息傳遞之Handler消息機制(一),Android消息傳遞之組件間傳遞消息(二)。html
消息傳遞相關文章地址:java
在作項目的時候每每須要應用程序內各組件間、組件與後臺線程間的通訊。好比耗時操做,等耗時操做完成後經過Handler或Broadcast將結果通知給UI,N個Activity之間須要經過Listener通訊,以前的實現方式咱們在Android消息傳遞之組件間傳遞消息(二)中已經介紹過了,其實這些均可以經過EventBus輕鬆實現,EventBus經過發佈/訂閱(publish/subscribe)方式來管理事件總線。其實EventBus的實現方式更加接近上篇文章的方式二,不一樣的是EventBus經過註解和反射機制 將訂閱者連同訂閱函數保存起來,而後在發送訂閱的時候 遍歷訂閱函數數組進行調用,其實從這方面就能夠EventBus執行效率多少會受到一點影響。android
EventBus出自greenrobot,和以前大名鼎鼎的GreenDao出自同一家。以前一直使用的是2.4版本,今天咱們將學習分析最新的Event 3.0,EventBus 3.0 最新的特性就是加入了註解,經過註解的方式 告知訂閱函數運行在哪一個線程中。git
github地址:https://github.com/greenrobot/EventBusgithub
官方文檔:http://greenrobot.org/eventbus/documentation數組
官網給出的各類角色的協做圖app
EventBus框架也是採用建造者模式設計的,能夠經過EventBusBuilder來設置一些配置信息,例如設置debug模式下要拋出異常框架
EventBus eventBus=EventBus.builder().throwSubscriberException(BuildConfig.DEBUG).build();
以前作圖片社交App的時候,須要處理一個點贊數據的同步,好比在做品的詳情頁點贊 須要同時更新列表頁該做品的點贊數量,這裏仍是以此爲例。異步
compile 'org.greenrobot:eventbus:3.0.0'
public class DataSynEvent { private int count; public int getCount() { return count; } public void setCount(int count) { this.count = count; } }
訂閱ide
EventBus.getDefault().register(this);//訂閱
解除訂閱
EventBus.getDefault().unregister(this);//解除訂閱
EventBus.getDefault().post(new DataSynEvent());
@Subscribe(threadMode = ThreadMode.MAIN) //在ui線程執行 public void onDataSynEvent(DataSynEvent event) { Log.e(TAG, "event---->" + event.getCount()); }
ThreadMode總共四個:
事件的優先級相似廣播的優先級,優先級越高優先得到消息
@Subscribe(threadMode = ThreadMode.MAIN,priority = 100) //在ui線程執行 優先級100 public void onDataSynEvent(DataSynEvent event) { Log.e(TAG, "event---->" + event.getCount()); }
發送有序廣播能夠終止廣播的繼續往下傳遞,EventBus也實現了此功能
EventBus.getDefault().cancelEventDelivery(event) ;//優先級高的訂閱者能夠終止事件往下傳遞
-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除了普通事件也支持粘性事件,這個有點相似廣播分類中的粘性廣播。自己粘性廣播用的就比較少,爲了方便理解成訂閱在發佈事件以後,但一樣能夠收到事件。訂閱/解除訂閱和普通事件同樣,可是處理訂閱函數有所不一樣,須要註解中添加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提供了一個EventBusAnnotationProcessor註解處理器來在編譯期經過讀取@Subscribe()註解並解析,
處理其中所包含的信息,而後生成java類來保存全部訂閱者關於訂閱的信息,這樣就比在運行時使用反射來得到這些訂閱者的
信息速度要快.
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" } }
此時編譯一次,自動生成生成索引類。在\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();
分別EventBus.getDefault().register(this);
添加以前:先後用了9毫秒
添加以後:先後用了2毫秒
優勢:簡化組件之間的通訊方式,實現解耦讓業務代碼更加簡潔,能夠動態設置事件處理線程以及優先級
缺點:目前發現惟一的缺點就是相似以前策略模式同樣的詬病,每一個事件都必須自定義一個事件類,形成事件類太多,無形中加大了維護成本
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兩個函數。
EventBus 2.x 是採用反射的方式對整個註冊的類的全部方法進行掃描來完成註冊,固然會有性能上的影響。EventBus 3.0中EventBus提供了EventBusAnnotationProcessor註解處理器來在編譯期經過讀取@Subscribe()註解並解析、處理其中所包含的信息,而後生成java類來保存全部訂閱者關於訂閱的信息,這樣就比在運行時使用反射來得到這些訂閱者的信息速度要快
EventBus 3.0的使用基本上總結完了,以前一直擔憂EventBus經過註解或者反射會影響太多性能,隨着3.0的發佈這部分影響已經很小了。