前言
世代在進步,技術在發(fā)展,當自己剛換了一份工作的時候,越來越多的聽到MVP,那么今天就趁此機會來學習一下MVP,對他進行一個徹底的了解
MVP概述
MVP其實同MVC一樣蚓耽,是一種編程模式和思想靖诗,也許更準確地講是一種架構语婴。
MVP分析
況且,隨著項目的深入發(fā)展,很多邏輯很越來越復雜航瞭,Activity處理的東西也會越來越多荤西,代碼越來越臃腫。這樣一來維護起來的代價就會越來越高敞掘,這是因為View的變化會引起Controller的很多變化,反之亦然楣铁。用一句大白話來說明就是–某一段代碼的變動會引起很多其他相關聯的代碼的改動玖雁,而程序員都是懶惰的,所以會恨死這樣的代碼盖腕。
而MVP就是要減輕在Android中的這種困惑赫冬。
View與Model并不直接交互浓镜,而是使用Presenter作為View與Model之間的橋梁。其中Presenter中同時持有Viwe層以及Model層的Interface的引用劲厌,而View層持有Presenter層Interface的引用竖哩。當View層某個界面需要展示某些數據的時候,首先會調用Presenter層的某個接口脊僚,然后Presenter層會調用Model層請求數據相叁,當Model層數據加載成功之后會調用Presenter層的回調方法通知Presenter層數據加載完畢,最后Presenter層再調用View層的接口將加載后的數據展示給用戶辽幌。這就是MVP模式的整個核心過程增淹。
MVP是基于MVC的,它的架構圖如下:
MVP是模型(Model)乌企、視圖(View)虑润、主持人(Presenter)的縮寫,分別代表項目中3個不同的模塊加酵。
- M(Model) 數據相關層,model是整個應用或界面的數據加工處理廠拳喻,所謂數據加工廠就是對數據的獲取,數據的解析猪腕,數據的存儲冗澈,數據的分發(fā),數據的增刪改查等操作陋葡。意思就是凡是涉及到數據操作都是在model進行的亚亲,所以model不僅僅只是實體類的集合,同時還包含關于數據的各種處理操作,負責處理數據的加載或者存儲腐缤,比如從網絡或本地數據庫獲取數據等捌归;
- V(View) 視圖層,就是用戶直接看到的界面,負責界面數據的展示岭粤,與用戶進行交互惜索;如Activity,一個view可以同時擁有多個presenter,也可以只有一個presenter
- P(Presenter) 紐帶層剃浇,首先presenter是處于mvp的中間層巾兆,將模型與視圖分離開來,用來連接Model與View會把view交給自己的命令進行一定的校驗等操作交給model處理,會把model處理的結果交給view,
MVP開發(fā)在Android中的基本流程
- View層定義View.interface偿渡,用來定義View的行為臼寄。一般由Activity或者是Fragment來實現這個接口,它定義了View視圖的各種變化溜宽,如設置Textview,加載對話框,更新進度條等质帅。
- Model層定義Modle.interface,這個是用來定義數據層發(fā)生變化時的通知接口适揉,因為Model不能直接與View交互留攒,所以它與Presenter交互,然后再通過Presenter間接達到與View的交互嫉嘀。
- Presenter翻譯的意思是主持人炼邀,也就是主持場合,控制節(jié)奏的意思剪侮。在這時Presenter就負責具體的業(yè)務邏輯拭宁,請求數據,把數據送到Model瓣俯,或者監(jiān)聽Model的數據變化杰标,接受View層的動作,負責通過通知View層的視圖變化彩匕。
如果跟MVC的架構圖對比的話腔剂,可以發(fā)現它們有相似之處也有不同。
相似之處
模塊劃分的相似
MVC由Model驼仪、View掸犬、Controller構成。
MVP由Model绪爸、View湾碎、Presenter構成。
不同的地方
- MVP中Presenter取代了MVC中的Controller
- MVC中Model、View、Controller之間相互發(fā)生通信栋荸,而MVP中Model與Presenter相互通信斜纪,View與Presenter相互通信,而Model與View之間沒有通信河哑。
Android中MVP的好處?
就Android層面上來講MVC架構雖然好,但不是最好廊遍,情況前面有講過。用一句話概括就是“模塊界限很模糊”贩挣。而MVP的出現實際上就是將MVC進行升級
1喉前、學習過設計模式的人都知道,這樣做基本符合了單一職責原則王财。
2卵迂、符合單一職責原則后,導致類與類組織更清晰绒净。
3见咒、對應Android開發(fā)中就是幫助Activity解壓,View層與Model層交互需要通過Presenter層進行,這樣v與m層級間的耦合性降低挂疆。
4改览、MVC中Activity同時充當了V和C的角色下翎,這就屬于界限劃分不清楚。而MVP則劃分的很清楚宝当,Activity只充當V的角色视事,業(yè)務邏輯控制交給了Presenter.通過這種分層處理,每一層的測試也相對簡單庆揩,維護性更高俐东。
個人對MVP模式的理解
這一段是我自己的看法,也許不正確订晌。
我個人覺得MVP沒有什么很神秘的虏辫,因為Android SDK上開發(fā),本來就差不多是MVC的角色腾仅。Activity基本上Android開發(fā)中最重要的一環(huán)乒裆。
我以前在團隊工作的時候,團隊分工是每人負責相應的Activity,在這里Activity是最小的開發(fā)單元推励。再后來鹤耍,某些Activity變得越來越重要,越來越復雜验辞,代碼也越來越多稿黄,這樣會造成團隊某個人的開發(fā)任務重,而其他的團隊成員也幫不上忙跌造。而MVP的出現可以將Activity再細分杆怕,劃為View和Presenter兩個部分,所以Activity不再是最小的開發(fā)單元壳贪,如果可以完全可以這樣分配任務陵珍,一個開發(fā)人員負責View部分,另一個開發(fā)人員負責Presenter部分违施。
況且因為MVP的劃分互纯,所以各個部分其實相對獨立,V的變動會對P的部分造成較少的影響磕蒲,而M對V或者說V對M幾乎是透明的留潦。
因為Presenter的存在,View和Model就可以很輕松辣往,頂多Presenter累一點兔院。
還有一個特點是MVP模式很適合測試,單獨測試VIEW成了一種可能站削。我們可以模擬View和Model的數據來測試Presenter的邏輯坊萝。
MVP實戰(zhàn)
所以,我用一個簡單的DEMO來講解,大家一看就明白屹堰。
場景需求
假設現在需要做一款APP肛冶,就是顯示天氣街氢,界面很簡單扯键,一個TextView顯示天氣信息,一個Button用來請求實時天氣珊肃。
如下圖所示
軟件啟動后荣刑,會自動獲取天氣,然后TextView就可以顯示信息伦乔。而用戶點擊獲取實時天氣的按鈕厉亏,界面上會彈出正在獲取中的進度對話框,等待數據加載成功后烈和,對話框消失爱只。Textview顯示就新的天氣情況。
代碼開發(fā),包的組織
因為選定MVP模式招刹,所以第一步就是包的組織恬试。
View層的接口定義及實現
在MVP中Activity用來專注視圖的表現。
而在本例子中View的表現有哪些呢疯暑?很多教程直接就上來貼代碼训柴,個人覺得這樣是不好的。View的表現當然要用View.interface接口來定義
現在我們來分析一下妇拯,在本例中View應該有哪些表現幻馁。
1.顯示天氣信息
接口方法定義
void onInfoUpdate(String info);
2.顯示獲取信息等待對話框
接口方法定義
void showWaitingDialog();
3.取消顯示對話框
接口方法定義
void dissmissWaitingDialog();
最終View層的接口定義就完成了,完整代碼如圖所示:
public interface IWetherView {
void onInfoUpdate(String info);
void showWaitingDialog();
void dissmissWaitingDialog();
}
接口文件已經定義好了,那么View的實現呢越锈?在這里用MainActivity去實現它仗嗦。
public class MainActivity extends AppCompatActivity implements IWetherView{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public void onInfoUpdate(String info) {
}
@Override
public void showWaitingDialog() {
}
@Override
public void dissmissWaitingDialog() {
}
}
具體的業(yè)務代碼,我們等會再實現甘凭。
Model層的接口定義及實現
Model層是數據層稀拐,用來存儲數據并且提供數據。在這里為了便于演示对蒲,數據被簡化為了String類型钩蚊。
接口定義如下:
public interface IWetherModel {
//提供數據
public String getInfo();
//存儲數據
public void setInfo(String info);
}
它的實現文件如下:
public class IWetherImpl implements IWetherModel {
@Override
public String getInfo() {
return null;
}
@Override
public void setInfo(String info) {
}
}
Presenter代碼及實現
Presenter是個大忙人,因為要同時對View和Model對接蹈矮,所以內部必須持有它們的接口引用砰逻。
所以有如下:
public class WetherPresenter {
IWetherModel mModel;
IWetherView mView;
}
Presenter與View的通信
View—–>Presenter
從視圖界面出發(fā),用戶要請求數據泛鸟,而Presenter是具體實現者蝠咆,所以Presenter要提供方法代View的實現者調用,并且View的實現中必須要有Presenter的引用。
所以MainActivity.java中要有WetherPresenter的引用刚操。
public class MainActivity extends AppCompatActivity implements IWetherView{
......
WetherPresenter mPresenter;
......
}
而Presenter也要開發(fā)API供View調用闸翅。
所以Presenter要有requestWetherInfo()方法:
public class WetherPresenter {
IWetherModel mModel;
IWetherView mView;
//供View層調用,用來請求天氣數據
public void requestWetherInfo(){
}
}
presenter—–>View
presenter操作View菊霜,是通過View.interface坚冀,也就是View層定義的接口。
所以很容易得到下面的代碼:
public class WetherPresenter {
......
private void showWaitingDialog(){
if (mView != null) {
mView.showWaitingDialog();
}
}
private void dissmissWaitingDialog(){
if (mView != null) {
mView.dissmissWaitingDialog();
}
}
private void updateWetherInfo(String info){
if (mView != null) {
mView.onInfoUpdate(info);
}
}
......
}
因為Presenter持有View的引用鉴逞,所以在這里要將View.interface注入到Presenter當中记某。
public class WetherPresenter {
IWetherModel mModel;
IWetherView mView;
......
public WetherPresenter(IWetherView mView) {
this.mView = mView;
}
......
}
Presenter與Model的通信
Presenter與Model的通信也是雙方的。
Presenter—->Model
presenter獲取到了數據构捡,可以交給Model處理
private void saveInfo(String info){
mModel.setInfo(info);
}
Model—–>Presenter
Model處理完數據后它也能對Presenter提供數據液南。Presenter可以通過Model對象獲取本地數據。
WetherPresenter.java
private String localInfo(){
return mModel.getInfo();
}
Presenter代碼實現
前面已經講了Presenter與Model,Presenter與View之間的通信勾徽,現在就可以編寫代碼將它們粘合起來滑凉。
Presenter本身需要向服務器獲取代碼,所以還要編寫它的相應方法:
public void requestWetherInfo(){
getNetworkInfo();;
}
private void getNetworkInfo(){
new Thread(new Runnable() {
@Override
public void run() {
try {
//打開等待對話框
showWaitingDialog();
//模擬網絡耗時
Thread.sleep(6000);
String info = "21度喘帚,晴轉多云";
//保存到Model層
saveInfo(info);
//從Model層獲取數據畅姊,為了演示效果,實際開發(fā)中根據情況需要啥辨。
String localinfo = localInfo();
//通知View層改變視圖
updateWetherInfo(localinfo);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//取消對話框
dissmissWaitingDialog();
}
}
}).start();
}
到此涡匀,完整的Presenter代碼如下:
public class WetherPresenter {
IWetherModel mModel;
IWetherView mView;
public WetherPresenter(IWetherView mView) {
this.mView = mView;
mModel = new IWetherModelImpl();
}
public void requestWetherInfo(){
getNetworkInfo();;
}
private void showWaitingDialog(){
if (mView != null) {
mView.showWaitingDialog();
}
}
private void dissmissWaitingDialog(){
if (mView != null) {
mView.dissmissWaitingDialog();
}
}
private void updateWetherInfo(String info){
if (mView != null) {
mView.onInfoUpdate(info);
}
}
private void saveInfo(String info){
mModel.setInfo(info);
}
private String localInfo(){
return mModel.getInfo();
}
private void getNetworkInfo(){
new Thread(new Runnable() {
@Override
public void run() {
try {
//打開等待對話框
showWaitingDialog();
//模擬網絡耗時
Thread.sleep(6000);
String info = "21度,晴轉多云";
//保存到Model層
saveInfo(info);
//從Model層獲取數據溉知,為了演示效果陨瘩,實際開發(fā)中根據情況需要。
String localinfo = localInfo();
//通知View層改變視圖
updateWetherInfo(localinfo);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//取消對話框
dissmissWaitingDialog();
}
}
}).start();
}
}
MainActivity代碼編寫
生成Presenter级乍。這個在Activity中的onCreate方法中舌劳,并把自身當成IWetherView注入到presenter當中。
mPresenter = new WetherPresenter(this);
2 . 操作Presenter玫荣。當用戶點擊按鈕時甚淡,通過調用mPresenter獲取數據,然后靜待更新捅厂。
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mPresenter.requestWetherInfo();
}
});
View.interface回調方法被觸發(fā)時贯卦,進行相應的視圖更新。
這里主要的視圖有
顯示對話框
取消對話框
顯示 天氣信息焙贷。
對應代碼如下:
@Override
public void onInfoUpdate(final String info) {
Log.d(TAG, "onInfoUpdate: "+info);
runOnUiThread(new Runnable() {
@Override
public void run() {
mTvInfo.setText(info);
}
});
}
@Override
public void showWaitingDialog() {
runOnUiThread(new Runnable() {
@Override
public void run() {
if(mDialog != null && mDialog.isShowing()){
mDialog.dismiss();
}
mDialog = ProgressDialog.show(MainActivity.this,"","正在獲取中...");
}
});
}
@Override
public void dissmissWaitingDialog() {
runOnUiThread(new Runnable() {
@Override
public void run() {
if(mDialog != null && mDialog.isShowing()){
mDialog.dismiss();
}
}
});
}
所以整個MainActivity.java代碼如下:
public class MainActivity extends AppCompatActivity implements IWetherView{
private static final String TAG = "MainActivity";
WetherPresenter mPresenter;
private TextView mTvInfo;
private Button mButton;
private ProgressDialog mDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPresenter = new WetherPresenter(this);
mTvInfo = (TextView) findViewById(R.id.tv_info);
mButton = (Button) findViewById(R.id.btn_request);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mPresenter.requestWetherInfo();
}
});
}
@Override
public void onInfoUpdate(final String info) {
Log.d(TAG, "onInfoUpdate: "+info);
runOnUiThread(new Runnable() {
@Override
public void run() {
mTvInfo.setText(info);
}
});
}
@Override
public void showWaitingDialog() {
runOnUiThread(new Runnable() {
@Override
public void run() {
if(mDialog != null && mDialog.isShowing()){
mDialog.dismiss();
}
mDialog = ProgressDialog.show(MainActivity.this,"","正在獲取中...");
}
});
}
@Override
public void dissmissWaitingDialog() {
runOnUiThread(new Runnable() {
@Override
public void run() {
if(mDialog != null && mDialog.isShowing()){
mDialog.dismiss();
}
}
});
}
}
總結
mvp非常適合大型的APP開發(fā)撵割,越復雜它的優(yōu)勢越明顯,但是如果APP代碼本身很簡明辙芍,mvp就有點繞彎子的感覺了啡彬。
完整代碼請點擊
參考代碼
SimpleNews