一種更清晰的Android架構

jopen 9年前發布 | 86K 次閱讀 Android Android開發 移動開發

一種更清晰的Android架構

>

  • 原文鏈接 : Architecting Android…The clean way?
  • 譯者 : Mr.Simple & Sophie.Ping </p>

    過去幾個月以來,通過在Tuenti網站上與@pedro_g_s和@flipper83(安卓開發兩位大牛)進行友好討論之后,我決定寫這篇關于架構安卓應用的文章。

    我寫這篇文章的目的是想把我在過去幾個月體悟到的小方法以及在調查和應用中學到的有用的東西分享給大家。

    入門指南

    大家都知道要寫一款精品軟件是有難度且很復雜的:不僅要滿足特定要求,而且軟件還必須具有穩健性,可維護、可測試性強,并且能夠靈活適應各種發展與變化。這時候,“清晰架構”就應運而生了,這一架構在開發任何軟件應用的時候用起來非常順手。

    這個思路很簡單:簡潔架構 意味著產品系統中遵循一系列的習慣原則:

    • 框架獨立性
    • 可測試
    • UI獨立性
    • 數據庫獨立性
    • 任何外部代理模塊的獨立性

    arch

    我們并不要求一定要用四環結構(如圖所示),這只是一個示例圖解,但是要考慮的是依賴項規則:源碼依賴項只能向內指向,內環里的所有項不能了解外環所發生的東西。

    以下是更好地理解和熟悉本方法的一些相關詞匯:

    • Entities:是指一款應用的業務對象
    • Use cases:是指結合數據流和實體中的用例,也稱為Interactor
    • Interface Adapters: 這一組適配器,是負責以最合理的格式轉換用例(use cases)和實體(entities)之間的數據,表現層(Presenters )和控制層(Controllers ),就屬于這一塊的。
    • Frameworks and Drivers: 這里是所有具體的實現了:比如:UI,工具類,基礎框架,等等。

    想要更具體,更生動豐富的解釋,可以參考這篇文章或者這個視頻

    場景

    我會設置一個簡單的場景來開始:創建一個簡單的小app,app中顯示從云端獲取的一個朋友或用戶列表。當點擊其中任何一個時,會打開一個新的窗口,顯示該用戶的詳細信息。這里我放了一段視頻,大家看看這個視頻 (國內或許不能訪問)大概就可以對我所描述的東西了解個大概了。

    Android應用架構

    這一對象遵循關注分離原則,也就是通過業務規則讓內環操作對外環事物一無所知,這樣一來,在測試時它們就不會依賴任何的外部元素了。
    要達到這個目的,我的建議就是把一個項目分成三個層次,每個層次擁有自己的目的并且各自獨立于堆放運作。
    值得一提的是,每一層次使用其自有的數據模型以達到獨立性的目的(大家可以看到,在代碼中需要一個數據映射器來完成數據轉換。如果你不想把你的模型和整個應用交叉使用,這是你要付出的代價)。

    以下是圖解,大家感受下:

    schema

    注:我并沒有使用任何的外部庫(除了用于json數據句法分析的gson和用于測試的junit, mockito, robolectric和espresso以外)。原因是它可以使這個示例更清晰。總之,在存儲磁盤數據時,記得加上ORM、依賴注入框架或者你熟悉的任 何工具或庫,這些都會帶來很大幫助。(記住:重復制造輪子可不是明智的選擇)

    表現層 (Presentation Layer)

    表現層在此,表現的是與視圖和動畫相關的邏輯。這里僅用了一個Model View Presenter(下文簡稱MVP),但是大家也可以用MVC或MVVM等模式。這里我不再贅述細節,但是需要強調的是,這里的fragment和 activity都是View,其內部除了UI邏輯以外沒有其他邏輯,這也是所有渲染的東西發生的地方。
    本層次的Presenter由多個interactor(用例)組成,用于完成Android UI線程以外的新線程的工作,并借助渲染到view中的數據callback函數來返回。
    mvp

    如果你需要一個使用MVP和MVVM的Effective Android UI典型案例,可以參考我朋友Pedro Gómez的文章。

    領域層 (Domain Layer)

    這里的業務規則是指所有在本層發生的邏輯。對于Android項目來說,大家還可以看到所有的interactor(用例)實施。這一層是純粹的java模塊,沒有任何的Android依賴性。當涉及到業務對象時,所有的外部組件都使用接口。

    domain

    數據層 (Data Layer)

    應用所需的所有數據都來自這一層中的UserRepository實現(接口在領域層)。這一實現采用了Repository Pattern,主要策略是通過一個工廠根據一定的條件選取不同的數據來源。
    比如,通過ID獲取一個用戶時,如果這個用戶在緩存中已經存在,則硬盤緩存數據源會被選中,否則會通過向云端發起請求獲取數據,然后存儲到硬盤緩存。
    這一切背后的原理是由于原始數據對于客戶端是透明的,客戶端并不關心數據是來源于內存、硬盤還是云端,它需要關心的是數據可以正確地獲取到。

    data

    >
    注:在代碼方面,出于學習目的,我通過文件系統和Android preference實現了一個簡單、原始的硬盤緩存。請記住,如果已經存在了能夠完成這些工作的庫,就不要重復制造輪子。

    錯誤處理

    這是一個長期待解決的討論話題,如果大家能夠分享各自的解決方案,那真真是極好的。
    我的策略是使用回調,這樣的話,如果數據倉庫發生了變化,回調有兩個方法:onResponse()和onError(). onError方法將異常信息封裝到一個ErrorBundle對象中: 這種方法的難點在于這其中會存在一環扣一環的回調鏈,錯誤會沿著這條回調鏈到達展示層。因此會犧牲一點代碼的可讀性。另外,如果出現錯誤,我本來可以通過 事件總線系統拋出事件,但是這種實現方式類似于使用C語言的goto語法。在我看來,當你訂閱多個事件時,如果不能很好的控制,你可能會被弄得暈頭轉向。

    測試

    關于測試方面,我根據不同的層來選擇不同的方法:

    • 展示層 ( Presentation Layer) : 使用android instrumentation和 espresso進行集成和功能測試
    • 領域層 ( Domain Layer) : 使用JUnit和Mockito進行單元測試;
    • 數據層 ( Data Layer) : 使用Robolectric ( 因為依賴于Android SDK中的類 )進行集成測試和單元測試。

    代碼展示

    我猜你現在在想,扯了那么久的淡,代碼究竟在哪里呢? 好吧,這就是你可以找到上述解決方案的github鏈接。還要提一點,在文件夾結構方面,不同的層是通過以下不同的模塊反應的:

    • presentation: 展示層的Android模塊
    • domain: 一個沒有android依賴的java模塊
    • data: 一個數據獲取來源的android模塊。
    • data-test: 數據層測試,由于使用Robolectric 存在一些限制,所以我得再獨立的java模塊中使用。

    結論

    正如 Bob大叔 所說:“Architecture is About Intent, not Frameworks” ,我非常同意這個說法,當然了,有很多不同的方法做不同的事情(不同的實現方法),我很確定,你每天(像我一樣)會面臨很多挑戰,但是遵循這些方法,可以 確保你的應用會:

    • 易維護 Easy to maintain
    • 易測試 Easy to tes.
    • 高內聚 Very cohesive.
    • 低耦合 Decoupled.

    最后,我強烈推薦你去實踐一下,并且分享你的經驗。也許你會找到更好的解決方案:我們都知道,不斷提升自己是一件件非常好的事。我希望這篇文章對你有所幫助,歡迎拍磚。

    ## 參考資料
    1. Source code: https://github.com/android10/Android-CleanArchitecture
    2. The clean architecture by Uncle Bob
    3. Architecture is about Intent, not Frameworks
    4. Model View Presenter
    5. Repository Pattern by Martin Fowler
    6. Android Design Patterns Presentation
 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!