MVP這個話題也是討論了很久很久的,熱度一直不減,甚至google官方也很認(rèn)可MVP在Android中的地位。
而我最近一個項目也參照google的那個MVP架構(gòu),發(fā)現(xiàn)并怎么好用。
果然架構(gòu)這種東西需要自己思考,根據(jù)不同的項目進(jìn)行變更,MVP MVC MVVM總的來說就是一個架構(gòu)模式,具體應(yīng)該怎么實現(xiàn)還是需要依賴于自己不斷的探索,照搬別人的項目的話是不可能適合你的項目的。所以網(wǎng)上的MVP項目也是五花八門,看得我欲仙欲死。
google工程師也說了,他們放出的那個Android Architecture只是作為參考,具體怎么實現(xiàn)mvp架構(gòu)取決于你。
而我也在這篇文章中聊一下我這一個月來照搬Google MVP的感受。

可以先看看這篇文章簡單的了解一下google官方mvp
Android官方MVP架構(gòu)示例項目解析
一、 V層:Fragment
google 用Fragment這個控件作為MVP中的View層,什么回事?
Fragment作為View,那么在Activity就可以初始化Presenter,View和其他一些依賴,讓依賴注入獨立于MVP外,Activity看起來就像一個Injector。如果Activity作為View的話,那么你需要在Activity中初始化一大堆依賴,View就不是單純的View了。
這雖然看起來很符合MVP的架構(gòu)思想,進(jìn)一步減少了View的工作。但實際上,不好的地方也是很多的。
寫項目的時候,不僅要多敲一個Fragment,而且在Activity中也是做反復(fù)性的工作:new Fragment,addFragment, new Present……
而且Fragment用起來并沒有Activity那么便利,還存在各種莫名其妙的bug,最致命的一點就是:
我們完全可以使用Dagger2進(jìn)行依賴注入,那么Fragment作為View的好處幾乎就沒有了。
在CleanArchitecture這個開源項目中,雖然也是用Fragment作為View層,但好歹別人用setRetainInstance()維持Presenter?。╬resenter生命周期,下面會講到),不過這方法用不好的話很容易導(dǎo)致內(nèi)存泄漏……
所以,最終我還是會選擇Activity作為View,除非某些頁面確實需要用到Fragment。
關(guān)于Fragment可以看看這篇文章:我為什么不主張使用Fragment
二、Contract協(xié)議類
google用一個Contract類定義了View層接口和Presenter層接口,這看起來是很不錯的設(shè)計,看起來很符合依賴倒置原則。
但是Presenter真的有必要實現(xiàn)接口嗎?
在MVP模式中,Presenter是高層次模塊,而View是低層次模塊。
而使用依賴倒置原則主要是為了高層和低層模塊的解耦,方便替換低層次模塊的時候不會影響到高層模塊,所以在MVP模式中,View需要實現(xiàn)接口,你可以隨時將View替換成Activity,F(xiàn)ragment或者其他,并不會印象到Presesnter。
View使用接口還有另外一個好處:方便測試。你不可能在對Presenter進(jìn)行單元測試的時候去Mock一個xxxActivity對吧……
而View雖然也依賴于Presenter,但是View和Presenter是一一對應(yīng)的,Presenter只會有一個,并不可能出現(xiàn)其他實現(xiàn),所以Presenter實現(xiàn)接口完全是多余的,不僅多了一個無意義的類,還累著自己。
并且,在View中想看Presenter代碼的時候,ctrl+左鍵點擊Presenter提示一大堆同名的Presenter。選擇其中一個點進(jìn)去之后,跳轉(zhuǎn)到的是contract類,最終結(jié)果你還是需要到目錄去找……
關(guān)于Presenter到底該不該使用接口解耦的一篇文章,我個人是非常認(rèn)同作者的。
不要再給MVP中Presenter寫接口了
三、P層的生命周期
這一塊也是MVP架構(gòu)中的一個難點
google的mvp很明顯沒有做額外的Presenter的生命周期管理,讓它隨著View的生命周期結(jié)束而結(jié)束。
這看起來沒什么問題,但是你們想想,Presenter往往會保存View的一些狀態(tài),比如保存是否已經(jīng)請求過數(shù)據(jù)的變量,或者某些請求參數(shù)。
當(dāng)Activity或者Fragment因為某些原因生命周期重走了(比如旋轉(zhuǎn)屏幕或者被系統(tǒng)回收),你的Presenter也會重新實例化一個。
如果用戶沒事做一直來回的旋轉(zhuǎn)屏幕,那么你的Presenter就一次次的實例化,一次次的請求網(wǎng)絡(luò)……最終可能導(dǎo)致內(nèi)存溢出或者ANR
不過我們國內(nèi)的app一般都禁止橫屏,所以這一點也不是很重要,但始終算是一個不好的地方吧。
對于延長Activity生命周期的方法有好幾種方式
可以看看這篇文章通過Loader延長Presenter生命周期
之前說的使用Fragment的setRetainInstance也是其中一種方法
四、Model層(Repository層)
在上面的圖中,也可以看到Google的Model層使用了多種數(shù)據(jù)源。
內(nèi)存,本地,網(wǎng)絡(luò)。
當(dāng)接收到Prestenr請求數(shù)據(jù)的時候,Repository就會從內(nèi)存中取數(shù)據(jù),如果沒有就從本地取,本地沒有就網(wǎng)絡(luò)請求。
也不用擔(dān)心每次請求到的數(shù)據(jù)都是緩存,因為Repository中還有一個變量判斷數(shù)據(jù)是否過期。
這種Model層的設(shè)計方式還是很不錯的,但是Repository中的代碼看起就有點混亂了。
如果單個Repository中的數(shù)據(jù)量比較多的情況下,那么看起來會非?;靵y,因為Repository中是管理了多個數(shù)據(jù)源的。
我覺得CleanArchitecture的model層設(shè)計比google的來的清晰,數(shù)據(jù)源用一個Factory類管理,并且用Cache類封裝緩存,可以管理緩存的過期時間等。比起google直接在Repository中處理數(shù)據(jù)源邏輯要好些。
還有另外一點就是,有些數(shù)據(jù)是不需要本地保存的,每次都應(yīng)該保證是最新的,例如新聞列表,這時候該怎么辦。
我個人認(rèn)為應(yīng)該統(tǒng)一風(fēng)格,就算不需要本地緩存,也務(wù)必使用多數(shù)據(jù)源結(jié)構(gòu)的Repository,雖然類會多了,但是可以讓你的項目更加清爽。
如果你的項目中,只有少數(shù)一兩個接口是需要本地緩存的,那么還是不要用多數(shù)據(jù)源的設(shè)計了。這是過度設(shè)計,畫蛇添足。
五、總結(jié)
googlesample的MVP架構(gòu)用來學(xué)習(xí)的話是很不錯的例子,至少比用其他人過度封裝的mvp要簡單點。
但是要在實際開發(fā)中使用mvp架構(gòu),最好是參照google和clean Architecture這兩個比較熱門的例子,然后花個幾天時間認(rèn)真思考才動手。
Model層,Presenter層的設(shè)計,還有統(tǒng)一處理公共參數(shù),統(tǒng)一處理異常等都是值得思考的東西。