曾露
摘要:傳統(tǒng)的MVC模式在Android的應用開發(fā)中存在諸多不足,主要表現(xiàn)在Android應用開發(fā)的關鍵類Activity會充當Controller和View的合體,既要負責業(yè)務邏輯,又要負責顯示,造成Activity的職責過多,耦合度高。MVP模式是MVC模式演進而來,引入了Presenter徹底分離Model和View層,在解決Activity臃腫的問題同時,還有助于后期的測試與維護。本文分析MVC對于Android開發(fā)的不足,并探索MVP模式在Android開發(fā)中的可行性,以及優(yōu)劣勢,最后實現(xiàn)MVP模式在Android開發(fā)中的應用。
關鍵詞:Android;MVP;模式
引言
GUI(Graphical User Interface)應用程序出現(xiàn)之后,應用程序也變得更加復雜,為了管理這種復雜性,基于職責分離的思想而孕育而出了MVC模式。在Android開發(fā)過程中,同樣會采用MVC模式的思想,將訪問和數(shù)據(jù)的表現(xiàn)分離。一般的處理是將進行界面描述的XML文件作為視圖層(View),使用的時候可以非常方便的引入,同時便于后期界面的修改。將Activity等類作為控制層(Controller),來控制View層和Model層的通信,以此來達到分離視圖顯示和業(yè)務邏輯層,其中與業(yè)務相關的數(shù)據(jù)結(jié)構(gòu)類作為模型層(Model),用來處理數(shù)據(jù)庫的操作、網(wǎng)絡請求等操作。但其實Activity并不是一個標準的MVC模式中的Controller,它的首要職責是加載應用的布局和初始界面,以及接受并處理來自用戶的操作請求,并做出響應。隨著界面及其邏輯的復雜度不斷提升,Activity類的職責不斷增加,以致變得龐大臃腫。不僅如此,在Android中,允許View和其它線程共存于Activity內(nèi),造成的問題是Activity中同時存在業(yè)務邏輯和UI邏輯,成為包羅萬象的“上帝類”,這大大增加了應用后期的測試和維護成本。為了使Android應用開發(fā)簡單,各層次職責清晰,增加可讀性和復用性,減少后期的測試、維護成本,本文在Android開發(fā)應用中引人MVP模式。
1 MVP模式
MVP從MVC演變而來,通過表示器將視圖與模型巧妙地分開。在該模式中,視圖通常由表示器初始化,它呈現(xiàn)用戶界面(UI)并接受用戶所發(fā)出命令,但不對用戶的輸入作任何邏輯處理,而僅僅是將用戶輸入轉(zhuǎn)發(fā)給表示器。通常每一個視圖對應一個表示器,但是也可能一個擁有較復雜業(yè)務邏輯的視圖會對應多個表示器,每個表示器完成該視圖的一部分業(yè)務處理工作,降低了單個表示器的復雜程度,一個表示器也能被多個有著相同業(yè)務需求的視圖復用,增加單個表示器的復用度。表示器包含大多數(shù)表示邏輯,用以處理視圖,與模型交互以獲取或更新數(shù)據(jù)等。模型描述了系統(tǒng)的處理邏輯,模型對于表示器和視圖一無所知。
1.1MVP模式的引入
在Android開發(fā)應用中,MVP的結(jié)構(gòu)劃分:視圖(View)負責繪制uI元素、與用戶交互,在Android開發(fā)中對應于Activity相關的類;模型(Model)類似于數(shù)據(jù)加工處理廠,負責對數(shù)據(jù)的獲取,數(shù)據(jù)的解析,數(shù)據(jù)的存儲,數(shù)據(jù)的分發(fā),數(shù)據(jù)的增刪改查等操作;表示器(Presenter)作為View與Model交互的中間紐帶,處于MVP的中間層,表示器會把視圖遞交的命令進行一定的校驗等操作,然后交給模型層處理,模型層處理完數(shù)據(jù)之后,會通知表示器,表示器主動去獲取數(shù)據(jù)處理的結(jié)果遞交給視圖層顯示。因此表示器有封裝業(yè)務,更新UI界面和持有線程等功能。各模塊數(shù)據(jù)的交互見圖1。
從上圖可以看出,MVP的分層結(jié)構(gòu)特別類似于網(wǎng)絡的七層協(xié)議,每層只知道自己依賴層的細節(jié)。層與層之間的耦合性低,模塊的復用性高,可維護性高,降低了測試的復雜度。
按照View和Presenter的交互方式和View本身的職責,可以將MVP劃分為PV(PassiveView)和SoC(Superviding Controller)。其中PV中的View是被動的,由Presenter來推送和獲取數(shù)據(jù),這也是普遍的用法,本文研究的MVP模式也屬此種模式。MVP模式的變種Passive View中各模塊的依賴關系如圖2所示。
在被動視圖(passive View)模式中,表示器通過接口與視圖交互。采用這種方案可以使表示器自身成為一個可重用性和可測試性均很高的類。首先,表示邏輯獨立于所使用的UI技術(shù),其次,針對某一接口為表示器編碼,該表示器可以與實現(xiàn)該接口的任何對象交互,而實現(xiàn)該接口的可能是Activity對象、Fragment對象等,這意味著只要視圖接口不變,視圖的任何更新都不會影響到表示器。這就能使單個表示器只專注于它自己的職責,使得表示器層結(jié)構(gòu)簡單,邏輯清晰,符合面向?qū)ο蟮膯我宦氊熢瓌t,當程序需要修改時,大大降低了修改的成本。再者,同一表示器可以處理同一應用程序的不同視圖。最后,如果將應用邏輯混合于UI代碼中,由于應用程序中的UI代碼非常難以自動測試,從而導致整個應用的難以測試。因此,從UI分離出視圖接口,將UI中的邏輯從視圖中移除,通過模仿視圖對象,可以方便地測試表示器。因此,表示器層是作為MVP體系的控制中心,而視圖僅僅是用戶交互請求的匯報者,不維護數(shù)據(jù)的狀態(tài)。表示器直接依賴View的接口。
1.2MVP模式的優(yōu)缺點
MVP與MVC的主要區(qū)別是View與Model不直接交互,而是通過與Presenter來完成交互,這樣可以修改視圖而不影響模型,達到解耦的目的,實現(xiàn)了Model和View真正的完全分離。視圖的變化總是比較頻繁,將業(yè)務邏輯抽取出來,放在表示器中實現(xiàn),使模塊職責劃分明顯,層次清晰,一個表示器能復用于多個視圖,而不需要更改表示器的邏輯(當然是在該視圖的改動不影響業(yè)務邏輯的前提下),這增加了程序的復用性。數(shù)據(jù)的處理由模型層完成,隱藏了數(shù)據(jù),在數(shù)據(jù)顯示時,表示器可以對數(shù)據(jù)進行訪問控制,提高數(shù)據(jù)的安全性。以前的Android開發(fā)是難以進行單元測試的,但是隨著項目變得復雜,測試時保證應用質(zhì)量的關鍵,MVP模式中,表示器對視圖是通過接口進行的,可以利用測試驅(qū)動,模擬出視圖對象,實現(xiàn)視圖相對于表示器的接口,就可以對表示層進行不依賴于UI環(huán)境的單元測試了,這大大降低了Android應用開發(fā)中的業(yè)務邏輯測試難度和復雜度。MVP模式的引人,視圖層完全不依賴與模型層,相當于將視圖從特定的業(yè)務場景中脫離出來,做到了對業(yè)務完全不可知的狀態(tài),因此可以將視圖層組件化,提供一系列接口供表示層操作,這樣就可以做出高度可復用的視圖組件了。
MVP的明顯缺點是增加了代碼的復雜度,特別是針對小型Android應用的開發(fā),會使程序冗余。Presenter中除了應用邏輯以外,還有大量的View->Model,Model->View的手動同步邏輯,會導致Presenter臃腫,維護困難。視圖的渲染過程也會放在Presenter中,造成視圖與Presenter交互過于頻繁,如果某特定視圖的渲染很多,就會造成Presenter與該視圖聯(lián)系過于緊密,一旦該視圖需要變更,那么Presenter也需要變更了,不能如預期的那樣降低耦合度和增加復用性。
2MVP模式的應用
2.1MVP模式中的模型
模型從黑盒的角度來說就是輸入/輸出數(shù)據(jù)。模型在Android應用中,主要負責從網(wǎng)絡,數(shù)據(jù)庫,文件,傳感器,第三方等數(shù)據(jù)源讀寫數(shù)據(jù),以及對外部的數(shù)據(jù)類型進行解析,轉(zhuǎn)換為應用程序內(nèi)部數(shù)據(jù),并交由上層處理,還對數(shù)據(jù)進行臨時存儲和管理,協(xié)調(diào)上層數(shù)據(jù)請求。
對于不同的數(shù)據(jù)處理,一般的,建立一個單例,進行數(shù)據(jù)的初始、設置、檢查以及處理。
public interface IManager{
void onAppCreate();//應用創(chuàng)建時
void set();//提供數(shù)據(jù)
void handle();//處理數(shù)據(jù)
void check();//數(shù)據(jù)檢查
}
在模型層中,會存在大量的實體,用于提供數(shù)據(jù)的存儲方式,如下所示:
public abstract class Entity{
private String mid;
public String getld(){
return mid;
}
protected abstract void setld(String id);
}
2.2 MVP模式中的視圖
前文提到,視圖層主要負責UI交互,在表示器的控制下修改UI,將業(yè)務事件交由表示器處理,不存儲數(shù)據(jù),也不與模型層交互。視圖層與表示器的任何交互都必須通過視圖接口進行,用戶的任何命令都必須轉(zhuǎn)發(fā)到表示器,并由其進行處理,因此要為每個視圖定義接口。
IView接口是每個視圖需要實現(xiàn)的接口,表示器通過此接口控制View,代碼如下所示:
publicinterfaceIView{
void initViews();//初始視圖
}
在Android應用中,用來顯示頁面的類一般是Activity、fragment等,這些類在使用時都需要繼承上述的接口,且需要依賴表示器,見BaseActivity所示:
public abstract class BaseActivity extends Frag-mentActivity{
private SetmAllPresenters;//一個activity有可能有多個IPresenter
protected abstract int getLayoutResld();//獲取layout的id
protected abstract void onlnitPresenters();//初始化presenters
Protected abstract void parseArgumentsFromln-tent(Intent argIntent);//從intent中解析數(shù)據(jù)
@Override
protected void
onCreate(Bundle savedln-stanceState){
super.onCreate(savedlnstanceState);
//周期onCreate
}
//…其他生命周期方法也是類似,調(diào)用IPresenter中相應的生命周期方法…
}
2.3 MVP模式中的表示器
表示器起到連接視圖與模型的橋梁作用,視圖中的控件將捕獲任何用戶操作并觸發(fā)視圖中的事件,例如按鈕單擊或索引選擇更改事件,視圖會直接向表示器遞交獲捕獲的數(shù)據(jù),交由表示器層處理。
表示器通常通過其初始函數(shù)接收對視圖的引用。視圖保留對表示器的引用,表示器保留對視圖接口的引用,表示器不依賴于具體的視圖對象。
首先將IView和IPresenter組合在一塊,建立IContract接口。
public interface IContract{
mtertace lConcreteView extends lVlew{
//具體view的UI操作
}
interface IConcretePresenter extends IPre-senter
//Presenter所需要處理的業(yè)務邏輯
}
}
表示器的初始函數(shù)接收并保存對視圖的引用,使用約定所表示的公共接口初始化視圖。表示器類還包含大量方法,執(zhí)行這些方法可響應來自UI的任何請求,任何單擊或用戶操作都與表示器類的方法綁定。表示器的接口還提供了對應于Activity或Fragment的生命周期的方法,這是為了根據(jù)視圖生命周期的不同提供相應的邏輯業(yè)務。如下所示:
public interface IPresenter
void onStop();
void onResume();
void onDestroy();
void onPause();
void onStart();
void init(V view);
}
3總結(jié)
在Android應用開發(fā)過程中,MVC框架并不能很好的契合Android的開發(fā)架構(gòu),因此本文采用MVP的模式開發(fā)Android應用,介紹了MVP模式應用于Android開發(fā)的可行性,同時通過編程,實現(xiàn)了MVP應用于Android開發(fā)的思想。具體在應用的過程中,還有很多許多細節(jié)需要注意,例如模型層對于復雜的數(shù)據(jù)處理,還需要根據(jù)不同的數(shù)據(jù)源、不同的業(yè)務請求而進行細分等。