Android常見的Event-driven方案

分析Android常用的Event-driven方案,分析各自的設計思路,使用方式,優勢劣勢;方便在項目中針對性使用,也爲設計自己的EDA(Event-driven Architecture)作鋪墊。

1.Listener

最普通的事件驅動設計,觀察者實現一個listener,注入到事件生產者中,獲取事件的回調。

1.1 解決的問題

將事件產生者,和事件監聽者解耦開來,使事件監聽者可替換,可增加減少。

1.2 使用例子


//1.實現接口
private listener = new OnXXXListener(){
    public void onXXX() {
        //do something
    }
}

//2.註冊監聽
XxxManager.registerOnXXXListener(listener);

//3.註銷監聽
XxxManager.unregisterOnXxxListener(listener);

//4.通知監聽者
if (listener != null){
    listener.onXXX();
}

1.3 優劣分析

優勢:
1. 機制簡單,高效;
2. 接口表意明確,易於理解。
劣勢:
1. 需要聲明一堆XXXListener,以及對應的註冊和註銷函數。
2. 強依賴XxxManager。
3. 不利於測試,如果要測試,必須對XxxMananger進行mock。

2.Basic Broadcast

使用Android提供的BroadcastReceiver組件,訂閱者需要繼承BroadcastReceiver類,並用IntentFilter來聲明自己關注的消息。消息默認在主線程中派發。

2.1 解決的問題

將事件產生者,和事件處理者之間的依賴徹底解耦,雙方都只需依賴系統提供的Context。並且BroadcastReceiver作爲基礎組件,可以用於跨APP,跨進程傳遞消息。

2.2 使用例子


//1.實現Receiver
public class XxxReceiver extends BroadcastReceiver{
    public void onReceive(Context context, Intent intent){
        //do something
    }
}


//2.註冊廣播(在AndroidManifest.xml中註冊)
<receiver android:name="com.xxx.XxxReceiver" >
    <intent-filter>
        <action android:name="xxx" />
    </intent-filter>
</receiver>


//2.註冊廣播(動態註冊)
XxxReceiver receiver = new XxxReceiver();
IntentFilter filter = new IntentFilter(action);
context.registerReceiver(receiver, filter);

//3.註銷廣播(僅針對動態註冊的Receiver)
context.unregisterReceiver(receiver);

//4.發佈廣播
Intent intent = new Intent(action);
context.sendBroadcast(intent);

2.3 優劣分析

優勢:
1. Android SDK支持,可以現成拿來使用;
2. 支持跨進程通知;
3. 使用Atlas時,未加載的bundle也能收到廣播,無縫兼容Atlas懶加載;
4. 發佈者和訂閱者基於字符串約定,解耦徹底;
5. 測試時只需要模擬廣播就可以了,測試方便。

劣勢:
1. 註冊廣播和發送廣播時操作比較麻煩;
2. 靜態註冊的廣播無法註銷;
3. 由於支持跨進程,支持跨APP,所以容易產生安全問題;
4. 消息派發的線程支持不如EventBus;
5. 由於是發送的廣播各個進程都會收到,因此如果幾個進程發送同一個消息時,容易混淆。

3.Local Broadcast

使用LocalBroadcast,也是Android SDK提供,使用方式和Basic Broadcast很類似,區別在於它是進程內部的,所以更加輕量級,也更加安全。

3.1 解決的問題

將事件產生者,和事件處理者之間的依賴徹底解耦,雙方都只需依賴LocalBroadcastManager。由於Basic Broadcast是重量級的廣播,LocalBroadcast的設計是爲了進程內部的使用場景。

3.2 使用例子


//1.定義一個Receiver
private BroadcastReceiver receiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent){
        //do something
    }   
}

//2.註冊廣播
IntentFilter filter = new IntentFilter(action);
LocalBroadcastManager.getInstance(context).registerReceiver(receiver, filter);

//3.註銷廣播
LocalBroadcastManager.getInstance(context).unregisterReceiver(receiver);

//4.發佈廣播
Intent intent = new Intent(action);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);

3.3 優劣分析

優勢:
1. 相比Basic Broadcast更加輕量級,由於是在進程內部的,所以更加安全。
2. Android SDK支持,可以現成使用;
3. 發佈者和訂閱者基於字符串約定,解耦徹底,測試方便;
4. 測試時只需要模擬廣播就可以了,測試方便。

劣勢:
1. 註冊廣播和發送廣播時操作比較麻煩;
2. 和Basic Broadcast相比,不支持跨進程通知;
3. 當使用Atlas時,如果bundle未加載,無法收到Local Broadcast;
4. 消息派發的線程支持不如EventBus。

4.Otto

Otto是square基於guava設計的EDA,和LocalBroadcast相比,他的訂閱者不需要實現框架提供的接口,只需要自己實現一個單參數的函數,並且有註解標記該函數處理event即可。

4.1 解決的問題

將事件產生者,和事件處理者之間的依賴徹底解耦,雙方都只需依賴Bus。其使用場景和LocalBroadcast是一致的,只是接口設計不太一樣,爲了使用上更加簡潔。

4.2 使用例子


//1.定義一個消息類型
public class XxxEvent{
    //some filds
}

//2.聲明Bus實例,一般是單例使用
Bus bus = new Bus();

//3.註冊訂閱者
//實現一個用來處理消息的函數,用@Subscribe標記,只能有一個參數
@Subscribe
public void onXxxEvent(XxxEvent event){
    //do something
}

//註冊
bus.register(this);

//4.註銷
bus.unregister(this);

//5.發佈消息
XxxEvent event = new XxxEvent();
bus.post(event);

4.3 優劣分析

優勢:
1. 發佈者和訂閱者,是基於消息的Class Type來約定的,相比字符串更加直觀;
2. 接口簡單易用;
3. SDK很小,接入成本低;
4. 和EventBus相比,用註解來標記消息處理函數,不容易出錯;

劣勢:
1. 發佈者和訂閱者之間的消息Class是耦合的,如果分模塊開發,解耦不如用字符串約定的徹底;
2. 訂閱的消息類型是通過消息的Class Type來區分的,因此每一個消息得聲明一個類,大規模使用會有很多消息Class;
3. 由於使用註解,和EventBus相比,效率更低。

5.EventBus

EventBus是greenrobot實現的EDA,其設計和Otto很像,不同之處在於,首先EventBus處理消息的函數是通過命名約定,而不是像Otto那樣的註解。其次,EventBus提供了更加豐富的線程支持;最後EventBus效率更高。

5.1 解決的問題

將事件產生者,和事件處理者之間的依賴徹底解耦,雙方都只需依賴EventBus。其使用場景和LocalBroadcast及Otto是一致的,只是接口設計不太一樣,同時提供更加豐富的線程支持。

5.2 使用例子


//1.定義一個消息類型
public class XxxEvent{
    //some filds
}

//2.註冊訂閱者
//接受事件的函數名必須叫onEvent
public void onEvent(XxxEvent event) {
    //do something
}

//註冊
EventBus.register(this);

//3.註銷監聽
EventBus.unregister(this);

//4.發送消息
XxxEvent event = new XxxEvent();
EventBus.post(event);

5.3 優劣分析

優勢:
1. 發佈者和訂閱者,是基於消息的Class Type來約定的,相比字符串更加直觀;
2. 接口簡單易用;
3. SDK很小,接入成本低;
4. 和EventBus相比,效率更高。

劣勢:
1. 發佈者和訂閱者之間的消息Class是耦合的,如果分模塊開發,解耦不如用字符串約定的徹底;
2. 訂閱的消息類型是通過消息的Class Type來區分的,因此每一個消息得聲明一個類,大規模使用會有很多消息Class;
3. 消息處理函數是通過命名約定的,容易寫錯,且因爲沒有實現接口,所以寫錯了也不能在編譯期發現問題。

總結

以上各種Event-driven方案各有特色,並沒有一種方案可以cover所有case,需要針對性去使用。例如在小模塊內部,用listener就顯得更加清晰,內聚。但是在比較鬆散的模塊間,用listener可能就不是一個好主意了。

參考引用

  1. Decoupling your Android code http://blog.android-develop.com/2014/03/decoupling-your-android-code.html
  2. Otto http://square.github.io/otto/
  3. EventBus https://github.com/greenrobot/EventBus
  4. Decoupling Android App Communication with Otto https://corner.squareup.com/2012/07/otto.html
  5. Event-driven programming for Android https://medium.com/google-developer-experts/event-driven-programming-for-android-part-i-f5ea4a3c4eab#.7bvzu9802
  6. Android開發中的Event Driven http://xhrwang.me/2015/05/09/event-driven-in-android.html
  7. What is Event-driven Architecture http://searchsoa.techtarget.com/definition/event-driven-architecture
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章