原文地址: http://www.itdecent.cn/p/ac51c9b88af3
qq群:301733278
前言(最后奉上福利)
自從Google在去年放出MVP的官方Sample后,越來越多的人開始加入MVP大軍,MVP可謂在16年大放異彩,我也乘勢(shì)推出了我的MVP框架狂刷了一波存在感
問題
但在使用當(dāng)中我也發(fā)現(xiàn)了諸多弊端,導(dǎo)致很多初學(xué)者,在寫過Sample后,就再也沒在自己的項(xiàng)目中使用過MVP
MVP需要?jiǎng)?chuàng)建太多的類和接口,并且每次通信都需要繁瑣的通過接口傳遞信息
這是大多數(shù)使用過MVP的朋友,最能感受到的,最近在幫公司技術(shù)面,我也時(shí)常問應(yīng)聘者,能否嘗試著解決這些問題?
解決方案
其實(shí)我之前已經(jīng)有一套解決方案,其實(shí)也不能叫解決,只能說是緩解
硬解決
所謂硬解決,便是使用比較暴力的方式,通過Template自動(dòng)生成需要的類和接口,這樣少去了頻繁的復(fù)制粘貼
軟解決
所謂軟解決,那就要?jiǎng)觿?dòng)腦子,稍微優(yōu)雅的解決了
對(duì)于邏輯簡(jiǎn)單的頁面可以不使用Presenter,直接在Activity或Fragment中處理邏輯,在Presenter中如果不需要處理數(shù)據(jù),也可以不使用Model
Presenter和Model都可以無限制的重用,所以MVP的劃分不需要太細(xì)粒度,稍微粗粒度一點(diǎn),即不需要每個(gè)Activity或Fragment都給他劃分一套MVP,可以幾個(gè)Activity或Fragment使用同一個(gè)Presenter(使用同一個(gè)類不是同一個(gè)對(duì)象,這個(gè)Presenter含有可以共用的邏輯),也可一個(gè)Activity或Fragment根據(jù)不同的需求持有多個(gè)不同類型的Presenter對(duì)象,Model層同理,這樣靈活使用,可以在一定程度上緩解MVP類和接口較多的缺點(diǎn)
并沒有完全解決問題
通過上面的解決方案,是可以一定的緩解MVP的缺點(diǎn),但是并不能完全解決上述缺點(diǎn)
比如想重用Presenter,Presenter就必須只含有公用的邏輯,而實(shí)際項(xiàng)目中公用的邏輯并不是那么多,所以能減少的類和接口也是很有限的,如果強(qiáng)制將不同頁面的邏輯放在同一個(gè)Prsenter中,來達(dá)到重用的目的,那么每個(gè)Activity會(huì)被迫實(shí)現(xiàn)許多并不需要的方法,得不償失
尋求解決方法
因此我看了大多數(shù)MVP框架,尋求如何徹底改善這個(gè)問題,像支付寶團(tuán)隊(duì)使用的TheMVP框架,是通過將Activity或Fragment作為Presenter,將UI操作抽到Delegate中,作為View層
TheMVP優(yōu)點(diǎn)
這樣做的好處是,不僅可以少寫很多類,而且Presenter直接就可以和Activity或Fragment的生命周期做綁定 (但使用 Google 最新發(fā)布的 Android 架構(gòu)組件當(dāng)中的 Lifecycles 就已經(jīng)可以非常簡(jiǎn)單的讓任何一個(gè)類與 Activity 或 Fragment 的生命周期做綁定, 包括 Presenter, 并且 Support Library v26.1.0 已經(jīng)內(nèi)嵌這個(gè)組件, 不用額外的引入這個(gè)組件), 且可以隨便重用View(但大多數(shù)場(chǎng)景都是重用Presenter,因?yàn)?strong>View層變化總是比其它層頻繁)
TheMVP缺點(diǎn)
缺點(diǎn)就是不能重用Presenter,并且對(duì)于Presenter的實(shí)現(xiàn)有限制,必須是Activity或Fragment,如果要在其他地方實(shí)現(xiàn)Presenter,如Adapter,Dialog就必須根據(jù)它的特性重新寫對(duì)應(yīng)的Presenter基類
因?yàn)?strong>Presenter基類繼承了Activity或Fragment,如果我們需要通過繼承使用其他Activity或Fragment,那就又需要修改Presenter基類,一旦某個(gè)Activity需要繼承其他不同的Activity,那又需要重新創(chuàng)建一個(gè)基于此Activity的Presenter基類,導(dǎo)致一個(gè)Activity或Fragment有多個(gè)不同的Presenter基類
分析問題,解決問題
總結(jié)一下MVP的缺點(diǎn)
1.粒度不好控制,控制不好就需要寫過多的類和接口
2.如要重用presenter可能會(huì)實(shí)現(xiàn)過多不需要的接口
3.Presenter和View通過接口通信太繁瑣,一旦View層需要的數(shù)據(jù)變化,那么對(duì)應(yīng)的接口就需要更改
想要在根本上解決以上問題,我想必須換個(gè)思路,能不能通過改變傳統(tǒng)MVP架構(gòu)來解決這些問題?
實(shí)現(xiàn)MVP現(xiàn)階段有兩種方式,各有優(yōu)缺點(diǎn):
一個(gè)是將Activity或Fragment作為Presenter,抽象一個(gè)View層出來
一個(gè)是將Activity或Fragment作為View,抽象一個(gè)Presenter層出來
我想達(dá)到重用Presenter的目的,自然選擇了后者
在某一天我突然想到了Handler,他只通過一個(gè)handleMessage方法,根據(jù)Message的what字段處理不同的操作,這樣向上層提供一個(gè)統(tǒng)一的入口,下層不管如何改變并不會(huì)影響上層,并且同樣可以實(shí)現(xiàn)多種的操作
于是根據(jù)這個(gè)思想,我重新改造了MVP架構(gòu),讓Presenter通過Message和View層通信
如何實(shí)現(xiàn)
先上張圖

具體做法是,VIEW層持有Presenter對(duì)象,當(dāng)用戶請(qǐng)求一個(gè)事件,則調(diào)用Presenter中的方法,并把持有View引用Message傳給此方法,此方法處理完請(qǐng)求邏輯后將數(shù)據(jù)封裝到Message中,并通過Message持有的View引用回調(diào)View的handleMessage方法,讓View做不同的操作,最后釋放掉Message的所有引用,放入消息池
Presenter并不直接持有View,方法執(zhí)行完即表示和View的關(guān)系解除
和Handler的原理很像,Handler是將消息放入MessageQueue,Looper去輪循處理消息,我這里是將消息放入,Presenter的方法,并立即處理消息
總結(jié)
這樣就能解決上述的缺點(diǎn):
少寫了很多類和接口
并且Presenter只需要通過handleMessage一個(gè)方法與View通信,也就不用繁瑣的一直添加接口方法,只需要一個(gè)Message參數(shù),通過Message封裝數(shù)據(jù),即使View需要的數(shù)據(jù)類型發(fā)生改變,也不需要更改任何方法,所以也不會(huì)影響上層調(diào)用
隨便重用Presenter,即使你一個(gè)Activity,重用10個(gè)不同的Presenter,那也只用實(shí)現(xiàn)一個(gè)handleMessage方法,不需要實(shí)現(xiàn)View中其他用不到的方法,通過一個(gè)方法同樣能做到不同的操作(傳統(tǒng)MVP一個(gè)頁面對(duì)應(yīng)一個(gè)Presenter,其實(shí)大多數(shù)Presenter只有一兩個(gè)方法,這樣導(dǎo)致存在大量代碼寥寥無幾的Presenter,你有想過將相近的邏輯都寫到一個(gè)Presenter中,一直重用Presenter有多爽嗎)
當(dāng)Presenter中的方法需要Activity傳遞一些數(shù)據(jù)時(shí),也可以將數(shù)據(jù)封裝到Message中傳給Presenter,這樣即使需要的數(shù)據(jù)類型發(fā)生改變,也不需要更改方法,所以也不會(huì)影響上層調(diào)用
只有能不斷的靈活重用,才能感受到MVP的強(qiáng)大之處
當(dāng)然很多不同的邏輯都寫在一個(gè)Presenter中,雖然可以少寫很多類,但是后面的擴(kuò)展性肯定不好,所以這個(gè)粒度需要自己控制,但是對(duì)于外包項(xiàng)目簡(jiǎn)直是福音
說了這么多還是要看看Demo,具體該怎么做吧?
公眾號(hào)
掃碼關(guān)注我的公眾號(hào) JessYan,一起學(xué)習(xí)進(jìn)步,如果框架有更新,我也會(huì)在公眾號(hào)上第一時(shí)間通知大家
Hello 我叫 JessYan,如果您喜歡我的文章,可以在以下平臺(tái)關(guān)注我
- 個(gè)人主頁: http://jessyan.me
- GitHub: https://github.com/JessYanCoding
- 掘金: https://juejin.im/user/57a9dbd9165abd0061714613
- 簡(jiǎn)書: http://www.itdecent.cn/u/1d0c0bc634db
- 微博: http://weibo.com/u/1786262517
-- The end