前言
去年5月左右的時候淹魄,筆者在逛GitHub的時候郁惜,看到了一個MVP的項目,叫做mosby甲锡,仔細看了源碼和作者介紹的文章后兆蕉,發(fā)現(xiàn)確實有點意思,雖然會需要多寫幾個類和方法缤沦,但是解決了activity/fragment過重的問題虎韵,通過V/P分離能夠幫助提高可維護性。時至去年年底缸废,今年年初包蓝,MVP才逐漸被大家所知,也不時看到些文章介紹其概念和實踐企量。
再說說MVVM (Model-View-ViewModel)测萎,在Android上對應data binding。即ViewModel到View的映射届巩,不需要再去自己找到view硅瞧,然后更新字段,而是在映射建立后直接更新ViewModel然后反映到View上恕汇。
值得一提的是零酪,MVP和MVVM都是微軟提出的理念冒嫡,最早都是在WPF里面被應用的,只是時至今日才在Android上被真正用起來四苇。本文不是來介紹這兩個的,所以不再贅述方咆,講講正題月腋。
在本系列首篇中我曾經(jīng)提到過在我設計的新應用中,采用了MVP+MVVM的混合(當初也考慮過Flux瓣赂,但感覺并不合適Android)榆骚,后來有一次CJJ同學和我探討這個架構(gòu)的時候,問到了我有沒有什么正式的名字煌集,我就有楞妓肢,因為這個組合和應用是我自己設計的,所以還真沒想過這個問題苫纤,Google一搜碉钠,還真有這么個東西(見參考資料佑钾,文章寫得很棒奄抽,建議英文不錯的同學讀一讀)!
這就是本文我要介紹的東西纺蛆,MVPVM (Model-View-Presenter-ViewModel)栗弟。
Quick glance
以下所有Model污筷,并不單單指的是Bean,而是Model層乍赫,更像是repository或者business logic瓣蛀。
MVC: View持有Controller,傳遞事件給Controller雷厂,Controller由此去觸發(fā)Model層事件惋增,Model更新完數(shù)據(jù)(如從網(wǎng)絡或者數(shù)據(jù)庫獲得數(shù)據(jù)后)觸發(fā)View的更新事件。
乍一看罗侯,MVP似乎是MVC的變種器腋,即C的位置被P取代了,但如果我們再看一看下圖:
其實MVP是MVC的一個wrap钩杰,C層仍然可以在那里纫塌,代替View處理點擊事件、數(shù)據(jù)綁定讲弄、扮演ListView的觀察者措左,從而View可以專注于處理純視覺的一些東西。而Presenter則避免了Model直接去觸發(fā)View的更新避除,View徹底成為了一個被動的東西怎披,只有Presenter告知其更新視覺胸嘁,它才會去更新,比如showLoading(), showEmpty()凉逛。
MVVM通過View和ViewModel的雙向綁定性宏,讓我們可以
- 直接更新ViewModel,View會進行對應刷新
- View的事件直接傳遞到ViewModel状飞,ViewModel去對Model進行操作并接受更新毫胜。
Why MVPVM
如果你仔細讀過Clean architecture的源碼,會發(fā)現(xiàn)其中已經(jīng)有了ViewModel這一層诬辈。如果你熟悉DO(Domain Object)酵使,PO(Persistent Object),VO(View Object)焙糟,或許了解visibility這個概念口渔,各層只需要知道其應該知道的。這些Object代表了完全獨立不同的概念穿撮。
ViewModel層的必要性缺脉,簡單舉個例子,服務器傳來一個Date String混巧,但我們需要顯示的是該Date到現(xiàn)在的相對時間描述枪向,比如1分鐘前,2天前咧党,為了避免在view中綁定數(shù)據(jù)時去做這個邏輯秘蛔,ViewModel會代替來進行這個的轉(zhuǎn)換。
MVVM盡管確實省去了綁定數(shù)據(jù)到View的boilerplate傍衡,但
- ViewModel引用了View深员,從而導致ViewModel無法重用于其他View。
- 并沒有解決View層過重的問題蛙埂,僅僅去掉了數(shù)據(jù)綁定倦畅,尤其對一些復雜業(yè)務邏輯的頁面。
模式的引入都是為了通過可拔插化以提高可復用性绣的,松耦合和盡量小的接口可以給予最大的可復用性叠赐,使得組件能重組使用。
所以有了MVPVM:
在我的個人實踐中:
- Model: data和domain模塊組成屡江,包含了Interactor(UseCase)芭概、Repository、Datastore惩嘉、Retrofit罢洲、Realm、DO文黎、部分PO等惹苗。
- View: Activity/Fragment殿较。
- Presenter:Presenter,包含了Subscriber桩蓉,并通過Dagger2注入UseCase從而減輕耦合淋纲。
- ViewModel:由Model轉(zhuǎn)換而成,繼承BaseObservable或SortedList院究,大部分直接wrap了model帚戳,從而去掉了mapper的boilerplate。通過Data Binding綁定到xml儡首。
從Presenter的Subscriber往下都是RxJava的流世界,stream in stream out偏友。如果你原來就應用了MVP或者Clean Architecture蔬胯,那會發(fā)現(xiàn)再加上ViewModel簡直太簡單了,同時讓代碼庫更小位他,邏輯更清晰氛濒。
接著看看各個組件在MVPVM中的standing。
MVPVM: Model
實際對應的是Repository層鹅髓,即第一篇文章中提到的data/domain module舞竿。具體的Model理論上應該是PO,但我們大部分場景并不需要PO窿冯,所以也可以是domain層的DO骗奖。
MVPVM: View
View對ViewModel不需要了解太多,這樣才能保持兩者的解耦醒串,兩者之間的協(xié)議只需要:
- ViewModel支持View需要展示的properties执桌。
- View實現(xiàn)了ViewModel的觀察者模式接口(如Listener)。
所以這里ViewModel到View是一條虛線芜赌,而不是MVVM中的雙向?qū)嵕€仰挣。
MVPVM: Presenter
和在MVP一樣,Presenter站在View和Model層之間缠沈。這里值得一提的是Presenter到ViewModel是有耦合的膘壶,因為Presenter需要把model更新到ViewModel中,也就是map行為洲愤,然后調(diào)用View的對應接口進行binding颓芭。
Presenter是MVPVM中唯一不需要解耦的,它緊緊地與View禽篱、ViewModel畜伐、Model層耦合。如果你的Presenter被多個View重用了躺率,那你可能需要考慮它是不是更應該作為一個module玛界,比如(第三方)登陸万矾。
MVPVM: ViewModel
MVPVM讓ViewModel可以重用,因為它再也不是直接和特定View綁定慎框,而僅僅作為數(shù)據(jù)到View的一個綁定用展示良狈。ViewModel因為用戶操作而觸發(fā)的事件不再直接對Model進行操作,而由View去負責任務流笨枯。ViewModel本身基本沒有field薪丁,而是通過暴露get方法來讓data binding找到對應要顯示的property,get方法中直接調(diào)用持有的model的對應屬性get方法馅精。
理想化的架構(gòu)是通過一個mapper類進行轉(zhuǎn)換严嗜,但我想大部分的程序員面對這個工作都會抓狂,畢竟很多字段其實就是一個復制洲敢,而且對性能也有一些影響(遍歷list漫玄,new對象,一個個字段轉(zhuǎn)換压彭,添加到新的list)睦优。所以折中地,讓ViewModel持有Model壮不,在get方法中直接返回對應model的具體字段汗盘,在一些特殊的field如相對時間、添加一些描述性字符的地方再去進行拼接和特殊處理询一。
啊隐孽,對了,說到ViewModel家凯,Data Binding現(xiàn)在支持雙向綁定了哦缓醋,見https://halfthought.wordpress.com/2016/03/23/2-way-data-binding-on-android/,語法如:
<EditText android:text="@={user.firstName}" .../>
不同于單向綁定的@{}绊诲,使用了@={}送粱,畢竟雙向綁定這個東西還是慎用,一方面早成數(shù)據(jù)流混亂不好理解掂之,另一方面容易出現(xiàn)死循環(huán)抗俄。
NO Presenter
在MVP中,我們有時候碰到的問題是世舰,Presenter真的有必要存在嗎动雹,尤其是一些較為靜態(tài),沒什么業(yè)務邏輯跟压,只需要純展示的頁面胰蝠,結(jié)果就是為了MVP而特意去創(chuàng)建一個Presenter。
所以Presenter不應該被強求,正如MVP中茸塞,V和C其實被并在了一起躲庄,在某些情況下(確實就是個純展示,或者很少的業(yè)務邏輯)钾虐,應該允許去Presenter噪窘,并讓View承擔其任務。比如注冊頁面效扫,我真的就只是想把用戶的輸入發(fā)到服務器驗證一下倔监,何必非得去搞一個presenter套著呢?
我們不能永遠理想化地去選擇所謂最好的設計菌仁,在現(xiàn)實的必要情況下浩习,我們要敢于舍棄,最合適的設計才是最好的設計济丘。為此瘦锹,Presenter不是強制的;為此闪盔,ViewModel并不一定通過mapper生成,而可以返回持有的DO對象對應字段辱士。
總結(jié)
本篇講了講MVPVM及其在Android的實踐泪掀,因為時間原因來不及寫個demo來說說具體實現(xiàn),歡迎大家提出意見和建議颂碘。有空的話我最近會在GitHub上寫一下demo异赫,你如果有興趣可以follow一下等等更新: markzhai。
下集預告
Dagger匕首头岔,比ButterKnife黃油刀鋒利得多塔拳。Square為什么這么有自信地給它取了這個名字,Google又為什么會拿去做了Dagger2呢峡竣?筆者看了很多國內(nèi)Dagger2的文章靠抑,但發(fā)現(xiàn)它們都保留在介紹API和官網(wǎng)翻譯的層面,無法讓讀者能明白究竟為什么用Dagger2适掰,又如何用好Dagger2颂碧。希望能在下一次為大家講清楚。
參考資料
原文:http://blog.zhaiyifan.cn/2016/03/14/android-new-project-from-0-p3/