在完成上一篇之后,斷斷續(xù)續(xù)的開始重構(gòu)我的Android項(xiàng)目代碼枪芒,現(xiàn)在終于完成了彻况。在重構(gòu)期間又仔細(xì)閱讀了一些開源項(xiàng)目的源碼及文章,并詢問了一些大神思路舅踪,按照理解自己完成了MVP結(jié)構(gòu)的重構(gòu)纽甘,與google samples項(xiàng)目的大致一致,但沒有完全照搬硫朦。本文側(cè)重一些重構(gòu)過程中思考的問題贷腕,,具體的代碼可以在Github查看咬展,本文的源碼為branch1.1泽裳,重構(gòu)前的是master,最好對比看看重構(gòu)的區(qū)別破婆。
對多重callback邏輯的思考
大量的文章都只介紹讀取一次網(wǎng)絡(luò)然后用一個監(jiān)聽接口處理訪問狀態(tài)涮总,這種是最常見的網(wǎng)絡(luò)訪問規(guī)則,如下圖所示:
但實(shí)際生產(chǎn)環(huán)境哪里僅僅是簡單的為列表獲取數(shù)據(jù)而已祷舀,要是完成業(yè)務(wù)邏輯需要多次網(wǎng)絡(luò)訪問呢瀑梗?來看一次登錄過程烹笔,我將每一個步驟都截圖如下:
1.基于UMENG SDK訪問微信獲取token抛丽,成功執(zhí)行下一步谤职。
2.基于UMENG SDK獲取用戶資料(頭像允蜈、昵稱),成功則執(zhí)行下一步蒿柳。
3.將數(shù)據(jù)發(fā)送服務(wù)器進(jìn)行登錄驗(yàn)證饶套,完成登錄業(yè)務(wù)。
說起來邏輯很簡單垒探,但是從代碼實(shí)現(xiàn)角度就不夠優(yōu)雅了妓蛮。在OnSuccess中執(zhí)行下一步動作,如果不寫在另一個方法函數(shù)中的話圾叼,那么看起來就是一層套一層的結(jié)構(gòu)蛤克,可讀性很差,我考慮的有幾種技巧來盡量提高可讀性:
1.添加充足的注釋
2.用Handler(之前我是這么做的)
3.用EventBus
4.用RxJava
在這次重構(gòu)中褐奥,我放棄了Handler的方式咖耘,如果注釋充足,Handler其實(shí)并沒有提高可讀性撬码,而且從MVP架構(gòu)的角度來思考儿倒,之前Handler的編寫并沒有將View邏輯與Presenter的邏輯分離,全部放在了Handler中呜笑,應(yīng)該盡量避免夫否。
EventBus在重構(gòu)中也未使用,因?yàn)镋ventBus的訂閱模式更適合一個操作需要通知多個處理的情況(比如收到新消息)叫胁,否則與監(jiān)聽接口相比閱讀性凰慈。并未提升太大。
我傾向使用RxJava驼鹅,雖然準(zhǔn)備在下一次重構(gòu)中才使用微谓,但已經(jīng)閱讀了不少的文章。在注釋充足的情況下输钩,現(xiàn)在callback套callback的方式已經(jīng)能夠閱讀豺型,但不可忽略的線程安全和內(nèi)存溢出問題,可以利用RxJava很好的解決(這也是放棄handler的一個原因买乃,你真以為實(shí)際生產(chǎn)中new一個出來就可以不管了么..)感興趣的朋友可以等我的下一個branch,我會來說說我的感受姻氨。
MVP重構(gòu)要一貫堅持到底么?
在google sample的todo例子中剪验,要想從列表頁打開新建頁肴焊,需要點(diǎn)擊浮動按鈕前联,這樣一個簡單的在onClick中搞定的事情,被拆分為三部分娶眷,我認(rèn)為實(shí)際有點(diǎn)過了似嗤。看看下面的代碼:
如果點(diǎn)擊消息列表的按鈕届宠,需要首先清除通知badage双谆,然后清除Preference的未讀數(shù)量,然后進(jìn)行頁面跳轉(zhuǎn)席揽。按照MVP架構(gòu)思路,清除通知badage應(yīng)該在View中實(shí)現(xiàn)(也就是在Activity里多一個方法)谓厘;清除未讀數(shù)量應(yīng)該在Model的Local中(也就是專門負(fù)責(zé)本地數(shù)據(jù)操作的類)幌羞;跳轉(zhuǎn)應(yīng)該在View中實(shí)現(xiàn)(Activity里面還要多寫一個方法)。
那么其實(shí)在OnClick中3行代碼搞定的時候竟稳,你要額外多謝那么多支撐框架的代碼属桦,有必要么?再看看Model層的一個例子:
在google sample的todo例子中他爸,網(wǎng)絡(luò)訪問與本地訪問分離聂宾,一條數(shù)據(jù)是從本地讀取還是從網(wǎng)絡(luò)讀取的邏輯判斷,是寫在Repository中的诊笤。如此毫無問題系谐,思路非常清晰。但如果不存在本地化的需要讨跟,可以把代碼直接寫在Repository中么纪他?(其實(shí)可以吧...反正也不影響閱讀)
todo例子中,網(wǎng)絡(luò)與本地的操作是保持一致的(基礎(chǔ)的增刪改查)晾匠,用接口來定義了方法茶袒。在實(shí)際生產(chǎn)中(比如登錄過程),本地與網(wǎng)絡(luò)的操作差別比較大凉馆,因此應(yīng)該根據(jù)實(shí)際判斷到底是否需要用接口定義方法薪寓。
todo例子中,用到了很多Ioc的思想澜共,如果你不用DI庫如dagger2或者要做單元測試向叉,可以考慮簡化一下,正如我實(shí)例化Presenter的代碼一樣咳胃。
重構(gòu)之后植康,數(shù)據(jù)的成員變量該放哪去了?
詳情頁展懈,需要一個描述商品數(shù)據(jù)的成員變量销睁;其他用戶的介紹頁供璧,需要一個描述用戶資料數(shù)據(jù)的成員變量;列表冻记,需要一個數(shù)組來保存數(shù)據(jù)睡毒,一個int來保存現(xiàn)在是第幾頁了。那么MVP結(jié)構(gòu)重構(gòu)之后冗栗,這些變量是不應(yīng)該仍然定義在Activity中的演顾,那么這些數(shù)據(jù)應(yīng)該放在MVP的那一層呢?
安裝todo例子中的介紹隅居,包含2方面的重構(gòu)钠至。首先,數(shù)據(jù)變量應(yīng)該寫在P層中胎源;其次棉钧,對ListView這種包含adapter的控件,數(shù)組操作應(yīng)該寫到Adapter中涕蚤。
以我一個Presenter為例宪卿,由于不考慮注入,因此Repository就直接在構(gòu)造器中自己實(shí)例化了万栅。當(dāng)前列表為第幾頁的mPage變量佑钾,是放在Presenter中了的,每次獲取數(shù)據(jù)后都直接將數(shù)據(jù)傳入View中烦粒,再在View中調(diào)用Adapter的自定義方法來更新列表(詳情見代碼)休溶。
在Adapter中,自定義了remove扰她、replace邮偎、addAll、getItem等幾種常用的數(shù)據(jù)操作函數(shù)义黎,不夠再加就是了禾进。自定義這些方法的時候,尤其要注意你是否自己實(shí)現(xiàn)了header-view的功能廉涕,如果實(shí)現(xiàn)了泻云,那么就要計算一下實(shí)際的位置(其實(shí)特簡單,一句話的事)狐蜕。
后話
其實(shí)本來想按照重構(gòu)后MVP長什么樣的思路來寫這文章宠纯,但是在重構(gòu)的過程中不斷的閱讀todo例子的代碼,覺得把握住幾個關(guān)鍵點(diǎn)层释,重構(gòu)的思路就會非常清晰:
1.各種數(shù)據(jù)操作都要放到M層中去婆瓜;
2.V層中只需要定義方法的時候把數(shù)據(jù)設(shè)為參數(shù),不管數(shù)據(jù)從哪來,到哪去廉白;
3.P層負(fù)責(zé)告訴M層要讀哪些數(shù)據(jù)(get)或者要向服務(wù)器發(fā)送什么數(shù)據(jù)(set)个初,根據(jù)數(shù)據(jù)訪問情況(成功或失敗)猴蹂,將數(shù)據(jù)傳給V層院溺;
這一篇更多的是想把自己重構(gòu)過程中的一些思考記下來和大家一起討論,本篇不是這次重構(gòu)的終點(diǎn)磅轻,下一篇我會繼續(xù)聊如何從這一步繼續(xù)跨到RxJava函數(shù)式編程的范圍去珍逸。每一次重構(gòu)是怎么變化的,代碼也給了聋溜,思路也給了谆膳,項(xiàng)目也是應(yīng)用商店上線的產(chǎn)品(雖然沒有錢推廣用的人不多),這個系列應(yīng)該比其他文章的玩具代碼參考價值更高吧撮躁。如有感興趣的大神指點(diǎn)一二摹量,或者也在前行的朋友一起來討論下,那我也覺得深夜碼字沒有白費(fèi)了馒胆。