本架構(gòu)是通過使用 RxJava + RxAndroid +Retrofit2 + Butterknife搭建的。
寫作目的
隨著項(xiàng)目逐漸壯大,必須有一個(gè)完善的框架作為一個(gè)支撐,不然難以維護(hù)。針對(duì)目前流行的框架,如:MVC,MVP,MVVM,我選擇了 MVP 作為 Android APP 整體架構(gòu)。
下圖為 MVP 架構(gòu)圖:
怕我沒說清楚,再具體一點(diǎn):
一開始,我也只是使用簡單的 MVP 模式(也就是一個(gè) activity 實(shí)現(xiàn)多個(gè) view 接口,同時(shí)一個(gè) activity 包含多個(gè) presenter,一個(gè) presenter 包含多個(gè) model,presenter 包含一個(gè) view。其實(shí)也就是標(biāo)準(zhǔn)型的 MVP 模式)。但是隨著項(xiàng)目逐漸壯大,presenter 里的代碼就越來越多,而且業(yè)務(wù)層代碼也越來越不清晰了。由于我的項(xiàng)目架構(gòu)是使用功能分類的,所以 presenter 也用功能分類,但由于 presenter 只包含一個(gè) view 導(dǎo)致所有需要該 presenter 的 activity 都只能使用同一個(gè) view,這樣 view 的耦合度就很高了。對(duì)于有代碼潔癖的我來說,實(shí)在有點(diǎn)難忍。于是我決定優(yōu)化一下。
在優(yōu)化之前,必須要知道設(shè)計(jì)框架目的是什么,設(shè)計(jì)框架絕對(duì)不能為設(shè)計(jì)而設(shè)計(jì)!
設(shè)計(jì)架構(gòu)的目的是什么:
通過設(shè)計(jì)使程序模塊化,做到模塊內(nèi)部的高聚合和模塊之間的低耦合。這樣做的好處是使得程序在開發(fā)的過程中,開發(fā)人員只需要專注于一點(diǎn),提高程序開發(fā)的效率,并且更容易進(jìn)行后續(xù)的測(cè)試以及定位問題。但設(shè)計(jì)不能違背目的,對(duì)于不同量級(jí)的工程,具體架構(gòu)的實(shí)現(xiàn)方式必然是不同的,切忌犯為了設(shè)計(jì)而設(shè)計(jì),為了架構(gòu)而架構(gòu)的毛病。同時(shí)希望設(shè)計(jì)的架構(gòu)能夠模塊化,希望把跟業(yè)務(wù)無關(guān)的東西抽象出來,開發(fā)人員根本只需要寫業(yè)務(wù)代碼就夠了。
整體思想
需要解決的問題:
- 讓 presenter 更加靈活,可以靈活使用到多個(gè) activity 中。
- 讓項(xiàng)目業(yè)務(wù)更加清晰,由于產(chǎn)品是迭代開發(fā)的,一般就兩周一個(gè)版本,必須方便迭代。
- 新增功能模塊必須方便,能形成模版,可以無腦使用。
下面開始介紹優(yōu)化內(nèi)容:
先上一個(gè)圖,作為一個(gè)簡單介紹:
如圖所示,view 的展示仍然由 activity 處理,presenter 仍然是中間人,model 還是業(yè)務(wù)邏輯層。不同的是,Activity 包含的 presenter 有多個(gè),并且是放在 SparseArray 數(shù)組里面的,presenter 里也包含多個(gè) view 和多個(gè) model,也都是放在 SparseArray 數(shù)組里面的。如何獲取具體的 presenter 或者 view、model 呢? 答案是通過 Action. Action 是重點(diǎn)。Action 放的是具體功能包含的操作。Action 從 Activity 傳入到 Presenter,通過 Action 在 Presenter 可以判斷用哪個(gè) View 來展示界面。
我不大會(huì)畫圖,可能大家會(huì)噴我了。
好吧,罵完了,我繼續(xù)裝逼了,舉個(gè)栗子吧:
比如說登錄功能,個(gè)人中心需要登錄、購物車結(jié)算也需要登錄,那么登錄就可以作為一種功能寫一個(gè) Presenter,而個(gè)人中心登錄需要展示登錄成功也需要展示登錄失敗,而購物車結(jié)算也許只需要登錄成功就可以了,而不需要展示登錄失敗,所以我需要有兩個(gè)不同的 View,一個(gè)是用于個(gè)人中心的,一個(gè)用于購物車結(jié)算。Action 怎么設(shè)置呢?就需要根據(jù)這個(gè)登錄功能設(shè)置兩個(gè) Action,因?yàn)槲覀冃枰诘卿?Presenter 里面根據(jù)不同的 Action 來選擇 View。Presenter 在 Activity 初始化的時(shí)候傳進(jìn)去,并且包含展示 View 的接口和 Action,于是 Presenter 就有 Action 了,當(dāng) Action 是來自于個(gè)人中心時(shí),Presenter 就可以根據(jù)個(gè)人中心登錄的 Action 調(diào)用它獨(dú)有的 View 來展示。當(dāng)是來自購物車結(jié)算頁面時(shí),也可以根據(jù) Action 來調(diào)用它獨(dú)有的 View。這樣就讓 Presenter 靈活了。
不過馬上有人會(huì)有問題了,這樣 Action 會(huì)不會(huì)很混亂?
是的,如果沒有統(tǒng)一管理的話確實(shí)會(huì)很混亂,這就是設(shè)計(jì) Contract 和 BaseAction 的理由。Contract 跟 Presenter 是成雙成對(duì)的。一個(gè) Presenter 對(duì)應(yīng)了一個(gè) Contract,Contract 里面就把 View、Presenter、Action 綁架到一起了,這樣便于管理同時(shí)便于修改。
我還害怕我沒說清楚,下面給部分代碼便于理解:
例如在登錄功能中,用戶中心登錄的操作。整體使用功能分類,如圖所示:
user 代表用戶中心,(原諒我英語不好,只能寫個(gè)簡單的單詞)
可見我這是通過功能分類。首先沖 Activity 來。
在 LoginActivity 初始化的時(shí)候,將所需要的 Presenter 都加入進(jìn)來,并且傳入 Action 將他們連接起來。如下圖:
這樣業(yè)務(wù)的 Activity 里面已經(jīng)木有了 Presenter, addPresenter 是來自基類(BaseActivity)的方法,這樣的好處在于我們可以統(tǒng)一管理 Presenter。
用戶登錄的 Action 可以這樣寫:
因?yàn)榭赡苡袃蓚€(gè) Activity 需要登錄功能,而需要展示不同的界面。所以寫了兩個(gè) Action。
像以前一樣 LoginActivity 可以實(shí)現(xiàn)多個(gè) View
但 View 已經(jīng)被統(tǒng)一根據(jù)具體功能放入了所屬的 Contract 里了。
LoginModel 寫起來也很簡單:
Model 跟以前的 MVP 的 Model 層比較沒什么差別,主要是考慮到 Model 并不需要抽得太多,Model 也是跟 Presenter 成雙成對(duì)來寫的。
好了,整體思想就是這樣的啦。賣個(gè)萌:
有人可能會(huì)想到會(huì)不會(huì)內(nèi)存泄漏,比如我這里使用 RxJava,在哪里取消訂閱呢?內(nèi)存在哪里釋放掉呢?
答案是:不會(huì)發(fā)生內(nèi)存泄漏,內(nèi)存釋放都在 BaseActivity 中處理,因?yàn)?Presenter 數(shù)組存在在 BaseActivity 中的。而且為了統(tǒng)一處理方便,presenter、view、model 都有一個(gè)唯一的基類。
那如何通過 Action 轉(zhuǎn)化成對(duì)應(yīng)的 Presenter、Model、View的?
答案是:通過泛型 + Class<T>,見下圖:
點(diǎn)擊下載源碼(github)
歡迎各位看官 star 我,同時(shí)有疑問可以隨時(shí)提,我可以隨時(shí)改善。最后,謝謝大家耐心看完!
————2020年3月11日 更新
最近建了微信公眾號(hào)和微博,由我(卷子)和我的好朋友(櫻桃)兩只小程序媛經(jīng)營的。
我們都喜歡程序員這個(gè)呆萌的群體,我們希望能給你帶來技術(shù)上的幫助以及生活上的快樂。嘿嘿~唯一的私心就是 希望你能喜歡我們咯。
大哥,我先敬你一瓶,先干為盡