1. 程式人生 > >Android 元件間的解耦

Android 元件間的解耦


EventBus、Otto, Android 自身提供的BroadcastReceiver/Intent System 和利用Handler實現的類似廣播功能
用來簡化應用元件間的通訊。

對比主要如下:

 Otto

主要使用Bus類和兩個註解@Produce, @Subscribe註解。
@Subscribe 註解告訴Bus該函式訂閱了一個事件,該事件的型別為該函式的引數型別;
@Produce 註解告訴Bus該函式是一個事件產生者,產生的事件型別為該函式的返回值。

 EventBus

Event Bus 與 Otto 相比,主要有3點不同:
1.事件的訂閱不是基於註解(Annotation)的,而是基於命名約定的。

在Android 4.0之前的版本中,註解解析起來比較慢,事件響應函式預設以“onEvent”開始,可以在EventBus中修改這個值,但是不推薦這麼幹。


2.事件響應有更多的執行緒選擇。

Otto預設就是在Main執行緒中執行。

EventBus可以向不同的執行緒中釋出事件,在ThreadMode列舉中定義了4個執行緒,只需要在事件響應函式名稱"onEvent"後面新增對應的執行緒型別名稱,則事件響應函式就會在對應的執行緒中執行。
比如事件函式"onEventAsync"就會在另外一個非同步執行緒中執行。
四種執行緒型別如下:
1.PostThread: 事件響應函式和事件釋出在同一個執行緒中執行。這個是預設值,這樣可以避免執行緒切換而帶來的消耗。
2.MainThread: 事件響應函式會在Android應用的主執行緒(UI執行緒)中執行。
3.BackgroundThread: 事件響應函式會在一個後臺執行緒中執行。如果事件釋出函式不是在主執行緒中,而會立即在事件釋出執行緒中執行響應函式。但如果事件釋出函式在主執行緒中,EventBus則會在唯一的一個後臺執行緒中按順序來執行所有的後臺事件響應函式。
4.Async: 事件響應函式在另外一個非同步執行緒中執行。該執行緒和釋出執行緒、主執行緒相互獨立。如果事件響應函式需要較長的時間執行,則應該使用該模式,例如網路訪問等。

需要注意的是,由於系統並行的限制,應該避免在同一時間觸發大量的非同步執行緒。EventBus使用一個執行緒池來提高執行緒的效率。

3.EventBus 支援Sticky Event
有時候某個事件可能會用到多次,可以把該事件釋出為Sticky Event,然後,當需要查詢該資訊的時候,可以通過Bus的getStickyEvent(ClassEventType)函式來查詢最新發布的Event物件。
同一型別的事件只儲存最新的Event物件。
註冊和釋出事件的函式分別為registerSticky() 和 postSticky()。

EventBus 可以解決大多數的需求,但是有沒有這樣一種需求??  我需要通知介面進行更新,但目前所在的介面又不需要進行更新,可不可以像這樣去更新:如果當前介面需要更新,則通知當前介面進行更新,否則不更新;通知介面需要更新的,如果待更新介面不在前臺,則不更新,待其被切換到前臺再進行檢查是否需要更新,即延遲更新。

3 利用Handler實現類似廣播的功能。

相對比較靈活,不管是在UI執行緒或者後臺執行緒, 都可以通過Handler傳送Message 通知相應的接受者進行相應的處理,也是一個不錯的選擇。

不足之處:
1) 一旦有增加或者刪除相應的事件,都要去修改類似訊息定義配置類檔案中的MSGID了。 經常改動
優點:
相對EventBus或Otto 不可以傳送延遲事件, 但Handler可以post delay訊息。

4 BroadcastReceiver/Intent System 

優點:

利用Android的自帶的廣播機制,不用像Otto, EventBus 或者Handler 專門去為了實現而寫大量東西,減少了開發成本;

缺點:
1)必須register/unregister, 否則會出現記憶體洩漏
2)耗時操作必須與service一起使用,否則ANR.

PS:Annotation註解

1.作用:
1)編譯期間檢查
Annotation具有“讓編譯器進行編譯檢查的作用”。
例如:@SuppressWarnings, @Deprecated和@Override都具有編譯檢查作用。

2)在反射中使用Annotation
在反射的Class,Method, Field等函式中,有許多Annotation相關的介面。這也意味著,可以在反射中解析並使用Annotation。

2. 組成部分:
每一個Annotation都與1個RetentionPolicy關聯,並且與“1~n個ElementType”關聯。可以通俗的理解為:
每1個Annotaion物件,都會有唯一的RetentionPolicy屬性;至於ElementType屬性,則有1~n個。
public enum ElementType {
    TYPE,               /* 類、介面(包括註釋型別)或列舉宣告  */


    FIELD,              /* 欄位宣告(包括列舉常量)  */


    METHOD,             /* 方法宣告  */


    PARAMETER,          /* 引數宣告  */


    CONSTRUCTOR,        /* 構造方法宣告  */


    LOCAL_VARIABLE,     /* 區域性變數宣告  */


    ANNOTATION_TYPE,    /* 註釋型別宣告  */


    PACKAGE             /* 包宣告  */
}

public enum RetentionPolicy {
    SOURCE,            /* Annotation資訊僅存在於編譯器處理期間,編譯器處理完之後就沒有該Annotation資訊了  */


    CLASS,             /* 編譯器將Annotation儲存於類對應的.class檔案中。預設行為  */


    RUNTIME            /* 編譯器將Annotation儲存於class檔案中,並且可由JVM讀入 */
}

2. Annotation 通用定義
@Documented
@Target(ElementType.Type)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation1 {

}
其中:
@interface 意味著它實現了java.lang.annotation.Annotation介面,即該註解就是一個Annotation。
定義Annotation時,@interface是必須的。 

@Documented
類和方法的Annotaion在預設情況下是不出現在javadoc中的,如果使用@Documented修飾該Annotation,則表示它可以出現在javadoc中。
定義Annotation時,@Documented可有可無;若沒有定義,則Annotation不會出現在javadoc中。

@Target(ElementType.TYPE)
ElementType是Annotation的型別屬性。而@Target的作用,就是用來指定Annotation的型別屬性。
定義Annotation時,@Target可有可無。若有@Target,則該Annotation只能用於它所指定的地方;若沒有@Target,則該Annotation可以用於任何地方。

@Retention(RetentionPolicy.RUNTIME)
RetentionPolicy 是Annotation的策略屬性,而@Retention的作用,就是指定Annotation的策略屬性。
定義Annotation時,@Retention可有可無。若沒有@Retention,則預設是RetentionPolicy.CLASS。