安卓開發:應用間通訊模式

sunbiaobiao發表於2015-06-14

安卓應用主要由Activities、Services、Fragments和輔助類等組成。然而,在元件之間進行通訊卻需要一些技巧。重用程式碼、解耦和即插即用對應用設計提出了更高的要求。通常,使用訊息匯流排(MessageBus)會是一種很好的選擇。

本文介紹了安卓開發中訊息匯流排概念、相關工具和庫函式,以及設計中需要考慮的注意事項。 

 安卓應用主要由Activities、Services、Fragments和輔助類等組成。然而,在元件之間進行通訊卻需要一些技巧。想要寫出鬆散耦合、能即插即用的可重用程式碼,也需要技巧。本文的目標是避免緊耦合。

緊耦合—元件彼此直接引用,直接互相呼叫, 在下面的程式碼中, 我們在 MenuFragment 中保留了一個對 MagazineActivity 的引用, 所以MenuFragment 就和 MagazineActivity 耦合在一起, 沒有MagazineActivity這個類, MenuFragment就無法執行。

這就導致了元件間大量直接的依賴, 這樣的設計中, 一個類的變化導致大量相關的類的變化, 維護起來相當棘手。 所以,如果是由多個開發人員共同開發,這個程式將會變的非常容易出錯。

所以,鬆耦合是以減少元件之間的依賴為目標,一個鬆耦合系統可以很輕鬆地把系統解構為多個不同的元素, 使得整個系統可以更有彈性和可維護。對於多個開發人員共同開發的場景, 每個開發人員只要開發相對獨立的模組就可以很輕鬆的和其他的模組通過標準協議相互聯絡。

傳統解耦方案:介面

介面對解耦很有幫助, 類之間通過介面相互通訊而不直接通訊,一個類可以提供一個介面給其他的類通訊, 這樣就只是提供了一個抽象層,而不用操心具體的實現, 這就像ATM機的人機互動一樣, 只要你會用鍵盤, 你就能在任何ATM機上進行交易,而不用管各個不同ATM機的內部實現細節。

下面是使用介面的一些缺點:

  1.  元件直接需要彼此瞭解才能傳遞介面,所以仍然會有一點依賴,
  2.  介面之間不能通過意圖傳遞 ,所以有時候android 的特定的元件之間不能相互通訊
  3.  在一個有很多通訊的應用中, 介面將會變得很臃腫, 導致你要寫大量的廢程式碼,徒增複雜度, 多個元件相互溝通的時候使用介面要鏈式傳遞訊息,例如,在一個Activity你的一個輔助類需要給一個Fragment傳遞資訊時,這將導致介面鏈

優雅的解決方案: 訊息通道

通訊是基於訂閱釋出的機制, 一個釋出者廣播一個事件, 訂閱者可以接到通知,並觸發動作。這樣可以做到純鬆耦合。

類似於ios中的 NSNotification 和 NSNotificationCenter 。

MessageBus的實現

1.基於implicit-Intent (隱藏意圖) 和BroadcastReceiver(廣播器)

基於implicit-Intent和BroadcastReceiver(廣播器)的通訊是一種訊息通訊的實現. 這個intent使用sendBroadcast() startActivity() 方法匹配action 並且 使用相應的IntentFilter過濾BroadcastReceivers 或者Activities, 它們會得到通知。有以下優點和缺點:

優點:可以聯絡不同的應用, 例如, 你的應用釋出一個展示圖片的implicit-Intent, 並且可以過濾選擇可以展示圖片的Activities。

缺點: Intent中的資料存放在bundle中,而bundles 不能存放複雜的資料結構, 自己實現Serializable或Parcelable介面開銷太大。

FluffyEvents 是一個基於implicit-Intent和BroadcastReceiver(廣播器)實現的訊息通訊庫

 2.EventBus: 基於MessageBus的事件

不同的事件可以被髮布和訂閱, 無論何時當一個訊息釋出者把訊息放到bus中, 一個訂閱者就會得到通知, 一個事件可以是任何java 類,  如下:

EventBus: 幕後工作:

EventBus使用map 資料型別跟蹤事件和活動類的訂閱者方法,Otto中這樣使用:

這個maping 只要下面兩步就搞定了

1.  使用反射,迴圈遍歷類中的方法
2.  如果一個基於註釋EventBus,遍歷註釋來填充map , 在基於命名約定的EventBus中,使用命名約定來填充對映

註釋: 註解處理在java中是一項高度密集的任務, 所以基於註解的EventBus會產生些許延遲

這個map在每次Bus上註冊類和登出類的時候更新,  通常一個Activity 或者 Fragment 在onRuesume方法中註冊, 在onPause方法中登出。輔助類需要在它們的主類(owning class),如Activity, Fragment或Service中註冊或者登出。當一個事件釋出到一個Bus上面,上述map就會被遍歷, 對映在Map上面的方法會被呼叫。

由於事件這個概念在Android中不是標準, 所以EnventBus 不能用於 app之間的通訊, 而且,Bus 類作用於一個應用之內。 目前Android沒有內嵌的Bus實現, 當然你可以自己實現一個,但是使用已經存在的類庫是更好的方式, 例如下面的兩個類庫:

Square’s Otto:  這是一個註解方式實現的通訊庫, 簡單易用, 基於Guava庫中的EventBus的實現, Otto是針對Android的優化,  提供執行緒選項但是很有侷限。

GreenRobot’s EventBus:  這是基於命名約定的EventBus, 但是效能上來看,優於其他類庫,有很多執行緒選項

結論:

權衡利弊總是少不了的, 在本文的討論中, 平衡點在於介面與EventBus的使用之間(在一個程式中兩者都會涉及), 介面導致廢程式碼,但目前是標準而高效的做法, EventBus使得程式碼簡潔, 但是反射和註解使得效能受損,   如果你不需要連線多個類, 你應該使用介面,EventBus應該用於多個不直接相連類之間的通訊。

相關文章