一种更清晰的Android架构
原文出處:http://www.kuqin.com/shuoit/20151106/348821.html?url_type=39&object_type=webpage&pos=1
過去幾個月以來,通過在Tuenti網(wǎng)站上與@pedro_g_s和@flipper83(安卓開發(fā)兩位大牛)進(jìn)行友好討論之后,我決定寫這篇關(guān)于架構(gòu)安卓應(yīng)用的文章。
我寫這篇文章的目的是想把我在過去幾個月體悟到的小方法以及在調(diào)查和應(yīng)用中學(xué)到的有用的東西分享給大家。
入門指南
大家都知道要寫一款精品軟件是有難度且很復(fù)雜的:不僅要滿足特定要求,而且軟件還必須具有穩(wěn)健性,可維護、可測試性強,并且能夠靈活適應(yīng)各種發(fā)展與變化。這時候,“清晰架構(gòu)”就應(yīng)運而生了,這一架構(gòu)在開發(fā)任何軟件應(yīng)用的時候用起來非常順手。
這個思路很簡單:簡潔架構(gòu) 意味著產(chǎn)品系統(tǒng)中遵循一系列的習(xí)慣原則:
- 框架獨立性
- 可測試
- UI獨立性
- 數(shù)據(jù)庫獨立性
- 任何外部代理模塊的獨立性
我們并不要求一定要用四環(huán)結(jié)構(gòu)(如圖所示),這只是一個示例圖解,但是要考慮的是依賴項規(guī)則:源碼依賴項只能向內(nèi)指向,內(nèi)環(huán)里的所有項不能了解外環(huán)所發(fā)生的東西。
以下是更好地理解和熟悉本方法的一些相關(guān)詞匯:
- Entities:是指一款應(yīng)用的業(yè)務(wù)對象
- Use cases:是指結(jié)合數(shù)據(jù)流和實體中的用例,也稱為Interactor
- Interface Adapters: 這一組適配器,是負(fù)責(zé)以最合理的格式轉(zhuǎn)換用例(use cases)和實體(entities)之間的數(shù)據(jù),表現(xiàn)層(Presenters )和控制層(Controllers ),就屬于這一塊的。
- Frameworks and Drivers: 這里是所有具體的實現(xiàn)了:比如:UI,工具類,基礎(chǔ)框架,等等。
想要更具體,更生動豐富的解釋,可以參考這篇文章或者這個視頻。
場景
我會設(shè)置一個簡單的場景來開始:創(chuàng)建一個簡單的小app,app中顯示從云端獲取的一個朋友或用戶列表。當(dāng)點擊其中任何一個時,會打開一個新的窗口,顯示該用戶的詳細(xì)信息。這里我放了一段視頻,大家看看這個視頻 (需翻墻)大概就可以對我所描述的東西了解個大概了。
Android應(yīng)用架構(gòu)
這一對象遵循關(guān)注分離原則,也就是通過業(yè)務(wù)規(guī)則讓內(nèi)環(huán)操作對外環(huán)事物一無所知,這樣一來,在測試時它們就不會依賴任何的外部元素了。
要達(dá)到這個目的,我的建議就是把一個項目分成三個層次,每個層次擁有自己的目的并且各自獨立于堆放運作。 值得一提的是,每一層次使用其自有的數(shù)據(jù)模型以達(dá)到獨立性的目的(大家可以看到,在代碼中需要一個數(shù)據(jù)映射器來完成數(shù)據(jù)轉(zhuǎn)換。如果你不想把你的模型和整個應(yīng)用交叉使用,這是你要付出的代價)。
以下是圖解,大家感受下:
注:我并沒有使用任何的外部庫(除了用于json數(shù)據(jù)句法分析的gson和用于測試的junit, mockito, robolectric和espresso以外)。原因是它可以使這個示例更清晰。總之,在存儲磁盤數(shù)據(jù)時,記得加上ORM、依賴注入框架或者你熟悉的任何工具或庫,這些都會帶來很大幫助。(記住:重復(fù)制造輪子可不是明智的選擇)
表現(xiàn)層 (Presentation Layer)
表現(xiàn)層在此,表現(xiàn)的是與視圖和動畫相關(guān)的邏輯。這里僅用了一個Model View Presenter(下文簡稱MVP),但是大家也可以用MVC或MVVM等模式。這里我不再贅述細(xì)節(jié),但是需要強調(diào)的是,這里的fragment和activity都是View,其內(nèi)部除了UI邏輯以外沒有其他邏輯,這也是所有渲染的東西發(fā)生的地方。 本層次的Presenter由多個interactor(用例)組成,Presenter在 android UI 線程以外的新線程里工作,并通過回調(diào)將要渲染到View上的數(shù)據(jù)傳遞回來。
如果你需要一個使用MVP和MVVM的Effective Android UI典型案例,可以參考我朋友Pedro Gómez的文章。
領(lǐng)域?qū)?(Domain Layer)
這里的業(yè)務(wù)規(guī)則是指所有在本層發(fā)生的邏輯。對于Android項目來說,大家還可以看到所有的interactor(用例)實施。這一層是純粹的java模塊,沒有任何的Android依賴性。當(dāng)涉及到業(yè)務(wù)對象時,所有的外部組件都使用接口。
數(shù)據(jù)層 (Data Layer)
應(yīng)用所需的所有數(shù)據(jù)都來自這一層中的UserRepository實現(xiàn)(接口在領(lǐng)域?qū)?#xff09;。這一實現(xiàn)采用了Repository Pattern,主要策略是通過一個工廠根據(jù)一定的條件選取不同的數(shù)據(jù)來源。 比如,通過ID獲取一個用戶時,如果這個用戶在緩存中已經(jīng)存在,則硬盤緩存數(shù)據(jù)源會被選中,否則會通過向云端發(fā)起請求獲取數(shù)據(jù),然后存儲到硬盤緩存。 這一切背后的原理是由于原始數(shù)據(jù)對于客戶端是透明的,客戶端并不關(guān)心數(shù)據(jù)是來源于內(nèi)存、硬盤還是云端,它需要關(guān)心的是數(shù)據(jù)可以正確地獲取到。
注:在代碼方面,出于學(xué)習(xí)目的,我通過文件系統(tǒng)和Android preference實現(xiàn)了一個簡單、原始的硬盤緩存。請記住,如果已經(jīng)存在了能夠完成這些工作的庫,就不要重復(fù)制造輪子。
錯誤處理
這是一個長期待解決的討論話題,如果大家能夠分享各自的解決方案,那真真是極好的。 我的策略是使用回調(diào),這樣的話,如果數(shù)據(jù)倉庫發(fā)生了變化,回調(diào)有兩個方法:onResponse()和onError(). onError方法將異常信息封裝到一個ErrorBundle對象中: 這種方法的難點在于這其中會存在一環(huán)扣一環(huán)的回調(diào)鏈,錯誤會沿著這條回調(diào)鏈到達(dá)展示層。因此會犧牲一點代碼的可讀性。另外,如果出現(xiàn)錯誤,我本來可以通過事件總線系統(tǒng)拋出事件,但是這種實現(xiàn)方式類似于使用C語言的goto語法。在我看來,當(dāng)你訂閱多個事件時,如果不能很好的控制,你可能會被弄得暈頭轉(zhuǎn)向。
測試
關(guān)于測試方面,我根據(jù)不同的層來選擇不同的方法:
展示層 ( Presentation Layer) : 使用android instrumentation和 espresso進(jìn)行集成和功能測試
領(lǐng)域?qū)?( Domain Layer) : 使用JUnit和Mockito進(jìn)行單元測試;
數(shù)據(jù)層 ( Data Layer) : 使用Robolectric ( 因為依賴于Android SDK中的類 )進(jìn)行集成測試和單元測試。
代碼展示
我猜你現(xiàn)在在想,扯了那么久的淡,代碼究竟在哪里呢? 好吧,這就是你可以找到上述解決方案的github鏈接。還要提一點,在文件夾結(jié)構(gòu)方面,不同的層是通過以下不同的模塊反應(yīng)的:
- presentation: 展示層的Android模塊
- domain: 一個沒有android依賴的java模塊
- data: 一個數(shù)據(jù)獲取來源的android模塊。
- data-test: 數(shù)據(jù)層測試,由于使用Robolectric 存在一些限制,所以我得再獨立的java模塊中使用。
結(jié)論
正如 Bob大叔 所說:“Architecture is About Intent, not Frameworks” ,我非常同意這個說法,當(dāng)然了,有很多不同的方法做不同的事情(不同的實現(xiàn)方法),我很確定,你每天(像我一樣)會面臨很多挑戰(zhàn),但是遵循這些方法,可以確保你的應(yīng)用會:
- 易維護 Easy to maintain
- 易測試 Easy to tes.
- 高內(nèi)聚 Very cohesive.
- 低耦合 Decoupled.
最后,我強烈推薦你去實踐一下,并且分享你的經(jīng)驗。也許你會找到更好的解決方案:我們都知道,不斷提升自己是一件件非常好的事。我希望這篇文章對你有所幫助,歡迎拍磚。
總結(jié)
以上是生活随笔為你收集整理的一种更清晰的Android架构的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: React Native
- 下一篇: JavaScript 知识图谱