編輯:關於Android編程
大家都知道要寫一款精品軟件是有難度且很復雜的:不僅要滿足特定要求,而且軟件還必須具有穩健性,可維護、可測試性強,並且能夠靈活適應各種發展與變化。這時候,“清晰架構”就應運而生了,這一架構在開發任何軟件應用的時候用起來非常順手。
這個思路很簡單:簡潔架構 意味著產品系統中遵循一系列的習慣原則:
框架獨立性 可測試 UI獨立性 數據庫獨立性 任何外部代理模塊的獨立性
我們並不要求一定要用四環結構(如圖所示),這只是一個示例圖解,但是要考慮的是依賴項規則:源碼依賴項只能向內指向,內環裡的所有項不能了解外環所發生的東西。
以下是更好地理解和熟悉本方法的一些相關詞匯:
Entities:是指一款應用的業務對象 Use cases:是指結合數據流和實體中的用例,也稱為Interactor Interface Adapters: 這一組適配器,是負責以最合理的格式轉換用例(use cases)和實體(entities)之間的數據,表現層(Presenters )和控制層(Controllers ),就屬於這一塊的。 Frameworks and Drivers: 這裡是所有具體的實現了:比如:UI,工具類,基礎框架,等等。
想要更具體,更生動豐富的解釋,可以參考這篇文章或者這個視頻。
場景
我會設置一個簡單的場景來開始:創建一個簡單的小app,app中顯示從雲端獲取的一個朋友或用戶列表。當點擊其中任何一個時,會打開一個新的窗口,顯示該用戶的詳細信息。這裡我放了一段視頻,大家看看這個視頻 (需翻牆)大概就可以對我所描述的東西了解個大概了。
Android應用架構
這一對象遵循關注分離原則,也就是通過業務規則讓內環操作對外環事物一無所知,這樣一來,在測試時它們就不會依賴任何的外部元素了。
要達到這個目的,我的建議就是把一個項目分成三個層次,每個層次擁有自己的目的並且各自獨立於堆放運作。
值得一提的是,每一層次使用其自有的數據模型以達到獨立性的目的(大家可以看到,在代碼中需要一個數據映射器來完成數據轉換。如果你不想把你的模型和整個應用交叉使用,這是你要付出的代價)。
以下是圖解,大家感受下:
注:我並沒有使用任何的外部庫(除了用於json數據句法分析的gson和用於測試的junit, mockito, robolectric和espresso以外)。原因是它可以使這個示例更清晰。總之,在存儲磁盤數據時,記得加上ORM、依賴注入框架或者你熟悉的任何工具或庫,這些都會帶來很大幫助。(記住:重復制造輪子可不是明智的選擇)
表現層 (Presentation Layer)
表現層在此,表現的是與視圖和動畫相關的邏輯。這裡僅用了一個Model View Presenter(下文簡稱MVP),但是大家也可以用MVC或MVVM等模式。這裡我不再贅述細節,但是需要強調的是,這裡的fragment和activity都是View,其內部除了UI邏輯以外沒有其他邏輯,這也是所有渲染的東西發生的地方。
本層次的Presenter由多個interactor(用例)組成,Presenter在 android UI 線程以外的新線程裡工作,並通過回調將要渲染到View上的數據傳遞回來。
如果你需要一個使用MVP和MVVM的Effective Android UI典型案例,可以參考我朋友Pedro Gómez的文章。
領域層 (Domain Layer)
這裡的業務規則是指所有在本層發生的邏輯。對於Android項目來說,大家還可以看到所有的interactor(用例)實施。這一層是純粹的java模塊,沒有任何的Android依賴性。當涉及到業務對象時,所有的外部組件都使用接口。
數據層 (Data Layer)
應用所需的所有數據都來自這一層中的UserRepository實現(接口在領域層)。這一實現采用了Repository Pattern,主要策略是通過一個工廠根據一定的條件選取不同的數據來源。
比如,通過ID獲取一個用戶時,如果這個用戶在緩存中已經存在,則硬盤緩存數據源會被選中,否則會通過向雲端發起請求獲取數據,然後存儲到硬盤緩存。
這一切背後的原理是由於原始數據對於客戶端是透明的,客戶端並不關心數據是來源於內存、硬盤還是雲端,它需要關心的是數據可以正確地獲取到。
注:在代碼方面,出於學習目的,我通過文件系統和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” ,我非常同意這個說法,當然了,有很多不同的方法做不同的事情(不同的實現方法),我很確定,你每天(像我一樣)會面臨很多挑戰,但是遵循這些方法,可以確保你的應用會:
易維護 易測試 高內聚 低耦合
最後,我強烈推薦你去實踐一下,並且分享你的經驗。也許你會找到更好的解決方案:我們都知道,不斷提升自己是一件件非常好的事。我希望這篇文章對你有所幫助,歡迎拍磚。
android-pdf-viewer在android studio應用問題說明小白一枚,之前一直是做.NET開發的,最近需要弄一個新聞app,能力有限,只能借助HTML5
前言在Activity中一般第一句就是調用setContentView(R.layout.XXX),但這其中系統做了那些工作?我們知道,在ClassLoader裝載了Ma
ES文件浏覽器連接電腦顯示登錄失敗。之前在連著電腦看著電視,不知道手機怎麼了,現在連接不上,還顯示登錄失敗。現在不知道怎麼辦。小編來給大家演示一下,當ES浏
一、Canvas Canvas中的方法很多,這裡我們只挑常用的進行講解說明 Canvas可以繪制的對象有: 弧線(arcs) canvas. 填充顏色(argb和co