Architecting Android…The clean way?

Architecting Android…The clean way?


過去幾個月,與@pedro_g_s 和 @flipper83 (順嘴說一下這兩位是android開發大牛)兩位同行在Tuenti 站點上友好的討論以後。我以爲這是一個寫一篇關於android應用架構的文章的好時機html

 
寫這篇文章的目的是想給你們展示一些我在這幾個月所想的加上從研究和實施中學到的一些小方法。java

入門指南

咱們知道寫一款有品質的軟件是困難和複雜的: 不只要知足要求,同一時候也是健壯的、可維護的、可測試的。而且足夠靈活適應擴展和變化。這時 「清晰架構」 就出現了,而且多是一個開發不論什麼軟件應用的好方法。android

這個思路很是easy:清晰架構表示產品系統遵循一組實踐原則:git

  • 框架獨立.
  • 可測試性.
  • UI獨立.
  • 數據庫獨立.
  • 不論什麼外部代理模塊獨立.

不必定非要使用四環結構 (如圖所看到的),因爲這僅僅是一個原理圖,但是你應該考慮依賴原則: 源代碼依賴僅僅能向內指向,並且內核中的所有項不能瞭解不論什麼外環的東西。github

下面是一些相關的詞彙用一個更好的方式熟悉和理解這些方法:數據庫

  • Entities: 應用的業務對象。
  • Use Cases: 結合業務對象的數據流入流出的用例. 相同被稱爲Interactors。

  • Interface Adapters: 這組適配器以最合適的格式轉換用例和業務對象之間的數據。Presenters(表現層) 和 Controllers(控制層)就屬於這裏。
  • Frameworks and Drivers: 這裏是詳細的實現:UI。工具類, 框架,等等。

想要更好更生動的解釋,參考這篇文章 或 視頻json

咱們的場景

我會用一個簡單的情景讓一切開始:建立一個簡單的APP用來顯示從雲端獲取的朋友列表和用戶檢索。當點擊它們的時候。一個新的界面爲用戶顯示具體信息。vim


我放了個視頻在這裏。這樣你對我所說的有個大概的映像:緩存

Android 架構

目標是 分離關注點。讓業務規則對外環事物一無所知,所以,它們在被測試時不需要依賴其餘外部元素。
要實現這個目標,個人 建議是將項目分爲三層,每一個都有本身的目的並且和其餘層分開工做。
值得一提的是,每一層都有本身的數據模型以達到這樣的獨立性(你會在看到在代碼中需要一個數據映射來完畢數據轉換,這需要付出一點代價,假設你不想把你的模型和整個應用交叉使用)。
這是圖示,你可以感覺一下:架構

clean_architecture_android

注意: 我沒有使用不論什麼外部庫(除了用於json數據的解析的gson和用來測試的junit、mockito、robolectric 和espresso)。 緣由是它可以使這個樣例更清晰。

無論怎樣不要猶豫加入ORMs存儲數據、依賴注入框架或者你熟悉的不論什麼類庫,這些都會讓你變得更輕鬆。(記住反覆造輪子是不明智的)。

Presentation Layer (表現層)

這裏, 表現的是邏輯和視圖動畫的關聯。 這裏用了一個Model View Presenter (下稱MVP)。但是你也可以用其餘不論什麼模式。像MVC 或者 MVVM。 我不會在這裏具體描寫敘述它們,但是這裏 fragments and activities 不過views,它們內部除了UI邏輯沒有其餘邏輯, 這也是所有渲染髮生的地方。

在這層的Presenters 由多個 interactors (用例) 組成,在android UI線程以外的新線程運行job,並經過回調將要渲染到view的數據取回。

clean_architecture_mvp

假設你需要一個使用MVP或者MVVM Effective Android UI 的炫酷的樣例,可以看看個人朋友 Pedro Gómez 所作的。

Domain Layer (領域層)

業務規則定義:所有的邏輯發生在這一層。 對於android項目,你也會看到所有的 interactors (用例) 在這裏實現。


這一層是一個純java的模塊,沒有不論什麼android依賴。

 所有的外部組件使用接口訪問業務對象。

clean_architecture_domain

Data Layer (數據層)

應用需要的所有數據來自這一層,經過UserRepository實現(這個接口在 domain layer),使用了 Repository Pattern做爲策略, 經過一個 factory 類,依據必定條件下選擇不一樣的數據源。
好比: 經過ID獲取用戶時。假設這個用戶在緩存中已經存在。則硬盤數據會被選中,不然 會從雲端獲取數據並保存在本地磁盤。


這一切背後的理念是數據源對client是透明的。 client不關心數據來源於內存、磁盤或者雲端。它僅僅關係數據會到達和被獲取到。

clean_architecture_data

注意: 出於學習的目的,這裏我實現了一個很easy的代碼,使用文件系統和android preferences 實現原始磁盤緩存,再次,假設已經存在能出色完畢這些工做的類庫,SHOULD NOT REINVENT THE WHEEL(不要反覆造輪子)

Error Handling (錯誤處理)

這是一個長期值得討論的主題。假設你可以分享你的解決方式那真實太好了。


個人策略是使用回調callbacks, 所以。 假如數據倉庫發送改變。回調callback有兩個方法 onResponse() 和onError(). 最後封裝異常的類叫 「ErrorBundle」: 這樣的方法會帶來一些困難,因爲有一個回調鏈一個接一個。直到錯誤到表現層呈現。

可讀性會有一點犧牲。


還有一方面。 假設出現錯誤,我使用event bus 系統拋出錯誤的事件。但是這類解決方式相似 GOTO,在我看來,當你訂閱多個事件但不能很是好的控制。你可能會懵掉。

Testing(測試)

關於測試。我依據不一樣的層選擇了幾個解決方式:

  • Presentation Layer(展現層): 使用android instrumentation 和 espresso 進行集成和功能測試。

  • Domain Layer(領域層): 使用JUnit 加 mockito 進行單元測試。

  • Data Layer(數據層): 使用Robolectric (這一層有android依賴) 加junit 加 mockito 進行集成和單元測試。

代碼展現

我猜你在想代碼在那裏? 好吧,這就是我上面講到內容的github鏈接

關於文件夾結構。提醒一下,不一樣的層使用模塊來表示:

  • presentation: 是一個android模塊表明展現層。
  • domain: 是一個沒有android依賴的java模塊。
  • data: 是一個android模塊,所有數據的獲取來源。
  • data-test: 數據層測試。由於使用Robolectric有一些限制問題,我不得不使用一個單獨的模塊。

結論

正如Bob大叔所說,「Architecture is About Intent, not Frameworks」 我全然統一這個說法。固然有不少不一樣的方式作這些事情(不一樣的實現方式),我很是確信天天你(像我)同樣會面臨很是多挑戰,但是使用上面的方法。可以確保你的應用會:

  • 易維護.
  • 易測試.
  • 高內聚.
  • 低耦合.

最後我強力推薦你去實踐一下,分享你的結果和經驗。或許你會找到更好的方法:咱們都知道持續改進 老是一個好的積極的事情。
我但願這篇文章對你有幫助,相同歡迎反饋不容許見。

Source code

  1. Clean architecture github repository – master branch
  2. Clean architecture github repository – releases

Further reading:

  1. Architecting Android..the evolution
  2. Tasting Dagger 2 on Android
  3. The Mayans Lost Guide to RxJava on Android
  4. It is about philosophy: Culture of a good programmer

Links and Resources

  1. The clean architecture by Uncle Bob
  2. Architecture is about Intent, not Frameworks
  3. Model View Presenter
  4. Repository Pattern by Martin Fowler
  5. Android Design Patterns Presentation
相關文章
相關標籤/搜索