1. 程式人生 > >Android中MVP模式講解及實踐

Android中MVP模式講解及實踐

前兩年的時候,我經常逛http://androidweekly.net這個網站,上面就有過很多文章介紹MVP模式,我很感興趣,於是把這個東西介紹給身邊的同事,同事們好像沒有多大反應,可能是當時在國內MVP用的範圍還比較少吧。後來我換了工作,再後來某一天我發現各類部落格紛紛在介紹這個東西,各類群裡面也在討論這個東西。我感覺到MVP被越來越多的人認可了,反倒是自己顯得落伍起來。所以,趁著空閒,做一個筆記,以做學習之路的備忘。

什麼是MVP模式?

看NBA的都知道MVP(National Basketball Association Most Valuable Player Award ,簡稱MVP)這個概念,我當時的第一反應也是這個。但是,此MVP非彼MVP.我們今天要討論的MVP其實同MVC一樣,是一種程式設計模式和思想,也許更準確地講是一種架構。

MVP和MVC

MVC簡介

開發Android的都知道MVC。

  • M對應Model,代表業務資料
  • V對應View,代表檢視
  • C對應Controller,代表控制器。

這裡寫圖片描述

MVC架構將檢視和資料分離,在WEB領域中應用的很廣泛。
使用者通過介面元件進行操作,也就是View層,相應的動作會傳遞給控制器也就是Controller層,而Controller根據自己的業務邏輯去操作資料層也就是Model,而最終資料層的變化會同步更新到檢視層。

MVC好處

這裡直接引用百度百科

MVC 分層有助於管理複雜的應用程式,因為您可以在一個時間內專門關注一個方面。例如,您可以在不依賴業務邏輯的情況下專注於檢視設計。同時也讓應用程式的測試更加容易。
MVC 分層同時也簡化了分組開發。不同的開發人員可同時開發檢視、控制器邏輯和業務邏輯。

可以看到MVC的主要目的是為了檢視和資料分離,這對於開發大型軟體來說更方便進行模組的劃分,提高編碼速度與質量。

Android中的MVC

Android世界中也經常運用到MVC模式。
Activity對應檢視介面也就是View層。
資料庫檔案,Sharedprefrence,記憶體緩衝,磁碟緩衝等資料內容對應Model層。
而Controller控制層基本上也由Activity層面來進行。

Android中mvc中基本動作流程

假設我們現在有這麼一個需求,需要在一個介面上顯示當天的天氣,不僅如此,還可以通過列表項選擇以往某一天的天氣。
mvc架構開發的話,大概是這樣。

  1. 在layout制定相應的佈局檔案,然後顯示在Activity上,用於顯示天氣資訊。這對應於View層,這裡的View並不是Android中開發中的元件view而是對檢視的統稱.
  2. Activity在onCreate方法或者onResume方法去伺服器獲取資料,或者通過介面上的某個按鈕之類去啟動獲取伺服器資料的任務,這裡就對應到View—>Controller,只不過這裡的View和Controller對是由Activity來完成。
  3. Controller獲取到了資料之後,分別存在,記憶體、磁碟和資料庫中,並且資料獲取成功或者失敗後,Activity介面需要同步更新狀態。這由對應上面流程中的Controller—>Model 和Model—->View。

這裡的流程還算清晰,也便於理解。

MVP為什麼?

上面講解了MVC的基礎知識,大家可能覺得MVC挺好的啊?怎麼還要整一個MVP。是的MVC是挺好的,但是它也有它的缺點,特別是針對Androi開發。

因為Android的特殊性,使得Activity對應了MVC中的V和C,同時擔任兩個角色,就有了類似“既當爹又當媽”的感覺,這顯然就不符合軟體設計原則的“單一職責”原則。但現實中是很多的APP程式碼中有這麼的處境,特別是Androi原生的很多系統APK,某些Activity動則幾千行程式碼。
況且,隨著專案的深入發展,很多邏輯很越來越複雜,Activity處理的東西也會越來越多,程式碼越來越臃腫。這樣一來維護起來的代價就會越來越高,這是因為View的變化會引起Controller的很多變化,反之亦然。用一句大白話來說明就是–某一段程式碼的變動會引起很多其他相關聯的程式碼的改動,而程式設計師都是懶惰的,所以會恨死這樣的程式碼。

而MVP就是要減輕在Android中的這種困惑。
MVP是基於MVC的,它的架構圖如下:
這裡寫圖片描述

  • M(Model) 資料相關層
  • V(View) 檢視層,如Activity上的佈局
  • P(Presenter) 紐帶層,用來連線Model與View.

MVP開發在Android中的基本流程
1. View層定義View.interface,用來定義View的行為。一般由Activity或者是Fragment來實現這個介面,它定義了View檢視的各種變化,如設定Textview,載入對話方塊,更新進度條等。
2. Model層定義Modle.interface,這個是用來定義資料層發生變化時的通知介面,因為Model不能直接與View互動,所以它與Presenter互動,然後再通過Presenter間接達到與View的互動。
3. Presenter翻譯的意思是主持人,也就是主持場合,控制節奏的意思。在這時Presenter就負責具體的業務邏輯,請求資料,把資料送到Model,或者監聽Model的資料變化,接受View層的動作,負責通過通知View層的檢視變化。

如果跟MVC的架構圖對比的話,可以發現它們有相似之處也有不同。

相似之處

模組劃分的相似
MVC由Model、View、Controller構成。
MVP由Model、View、Presenter構成。

不同的地方

  1. MVP中Presenter取代了MVC中的Controller
  2. MVC中Model、View、Controller之間相互發生通訊,而MVP中Model與Presenter相互通訊,View與Presenter相互通訊,而Model與View之間沒有通訊。

Android中MVP的好處?

就Android層面上來講MVC架構雖然好,但不是最好,情況前面有講過。用一句話概括就是“模組界限很模糊”。而MVP的出現實際上就是將MVC進行升級,對應Android開發中就是幫助Activity解壓。
MVC中Activity同時充當了V和C的角色,這就屬於界限劃分不清楚。而MVP則劃分的很清楚,Activity只充當V的角色,業務邏輯控制交給了Presenter.

個人對MVP模式的理解

這一段是我自己的看法,也許不正確。
我個人覺得MVP沒有什麼很神祕的,因為Android SDK上開發,本來就差不多是MVC的角色。Activity基本上Android開發中最重要的一環。
我以前在團隊工作的時候,團隊分工是每人負責相應的Activity,在這裡Activity是最小的開發單元。再後來,某些Activity變得越來越重要,越來越複雜,程式碼也越來越多,這樣會造成團隊某個人的開發任務重,而其他的團隊成員也幫不上忙。而MVP的出現可以將Activity再細分,劃為View和Presenter兩個部分,所以Activity不再是最小的開發單元,如果可以完全可以這樣分配任務,一個開發人員負責View部分,另一個開發人員負責Presenter部分。
況且因為MVP的劃分,所以各個部分其實相對獨立,V的變動會對P的部分造成較少的影響,而M對V或者說V對M幾乎是透明的。
因為Presenter的存在,View和Model就可以很輕鬆,頂多Presenter累一點。
還有一個特點是MVP模式很適合測試,單獨測試VIEW成了一種可能。我們可以模擬View和Model的資料來測試Presenter的邏輯。

MVP實戰

在現在的公司專案中,我已經用上了MVP模式開發。但是在這裡,我不想照搬程式碼。主要是因為怕複雜的程式碼或者其它的知識點干擾MVP本身的脈絡。所以,我用一個簡單的DEMO來講解,大家一看就明白。

場景需求

假設現在需要做一款APP,就是顯示天氣,介面很簡單,一個TextView顯示天氣資訊,一個Button用來請求實時天氣。
如下圖所示
這裡寫圖片描述

軟體啟動後,會自動獲取天氣,然後TextView就可以顯示資訊。而使用者點選獲取實時天氣的按鈕,介面上會彈出正在獲取中的進度對話方塊,等待資料載入成功後,對話方塊消失。Textview顯示就新的天氣情況。
這裡寫圖片描述

程式碼開發

因為選定MVP模式,所以第一步就是包的組織。
這裡寫圖片描述

View層的介面定義及實現

在MVP中Activity用來專注檢視的表現。
而在本例子中View的表現有哪些呢?很多教程直接就上來貼程式碼,個人覺得這樣是不好的。View的表現當然要用View.interface介面來定義
現在我們來分析一下,在本例中View應該有哪些表現。

1.顯示天氣資訊

那好,介面方法可以這樣定義。

public void onInfoUpdate(String info);
2.顯示獲取資訊等待對話方塊

介面可以這樣寫

public void showWaitingDialog();
3.取消顯示對話方塊
public void dissmissWaitingDialog();

最終View.interface就完成了,非常簡單

public interface IWetherView {

    public void onInfoUpdate(String info);

    public void showWaitingDialog();

    public 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() {

    }
}

具體的業務程式碼,我們等會再實現。

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

從檢視介面出發,使用者要請求資料,而Presenter是具體實現者,所以Presenter要提供方法代View的實現者呼叫,並且View的實現中必須要有Presenter的引用。
所以MainActivity.java中要有WetherPresenter的引用。

public class MainActivity extends AppCompatActivity implements IWetherView{
    ......
    WetherPresenter mPresenter;
    ......
}    

而Presenter也要開發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層獲取資料,為了演示效果,實際開發中根據情況需要。
                    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層獲取資料,為了演示效果,實際開發中根據情況需要。
                    String localinfo = localInfo();

                    //通知View層改變檢視
                    updateWetherInfo(localinfo);

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    //取消對話方塊
                    dissmissWaitingDialog();
                }
            }
        }).start();
    }
}

MainActivity程式碼編寫

  1. 生成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();
            }
 });
  1. View.interface回撥方法被觸發時,進行相應的檢視更新。
    這裡主要的檢視有
    • 顯示對話方塊
    • 取消對話方塊
    • 顯示 天氣資訊。

對應程式碼如下:

@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開發,越複雜它的優勢越明顯,但是如果APP程式碼本身很簡明,mvp就有點繞彎子的感覺了。

附錄

這裡寫圖片描述

相關推薦

AndroidMVP模式講解實踐

前兩年的時候,我經常逛http://androidweekly.net這個網站,上面就有過很多文章介紹MVP模式,我很感興趣,於是把這個東西介紹給身邊的同事,同事們好像沒有多大反應,可能是當時在國內MVP用的範圍還比較少吧。後來我換了工作,再後來某一天我發

AndroidMVP模式講解

       UI層越來越複雜,為了減輕了UI層的責任,也是為了更好地細分檢視(View)與模型(Model)的功能,讓View專注於處理數 據的視覺化以及與使用者的互動,讓Model只關係資料的處理,MVP(Model-View-Presenter)模式

關於Android通知欄的講解適配Android 8.0

前言 通知欄是Android系統原創的一個功能,雖然喬布斯一直認為Android系統是徹徹底底抄襲IOS的一個產品,但是通知欄確實是Android系統原創的,反而蘋果在ios 5之後也加入了類似的通知欄功能。 通知欄的設計非常巧妙,他預設情況下不佔用任何空間,只有當用戶需要的時候用手

淺談AndroidMVP模式與MVC模式的區別

一、概述 對於MVP(Model View Presenter),大多數人都能說出一二:“MVC的演化版本”,“讓Model和View完全解耦”等等。本篇博文僅是為了做下記錄,提出一些自己的看法,和幫助大家如何針對一個Activity頁面去編寫針對MVP風

[Android學習]AndroidMVP模式初探1

前言: 1. 初識MVP模式時,看到它缺點是需要增加一倍的類的維護量。所以就暫時沒用它。但是,當一個類的程式碼行數達到一定的量(1000行以上),這時候維護類變得好麻煩,主要是功能變得多了,方法數量也變多了。這個時候真的是需要給類“瘦瘦身”。

Android開發MVP模式

傳統的開發模式mvc大家都很熟悉。 View負責頁面展示,Model負責資料。 Controller一個控制協調前兩者的關係,很常見,耦合關係也很明顯。 在常見的android應用開發中Activity類可以是非常複雜的程式碼集合,裡面有各種view,事件,網路請求,資料bean。

Android設計模式MVC,MVP,MVVM的簡單理解

設計模式VS框架框架是程式碼的重用,可擴充套件。舉幾個簡單的例子。Spring架構,Struts架構。設計模式是設計的重用,是一種抽象的設計方法。例如MVC,MVP,MVVM。下面,我們以android開發為例,簡單比較一下三種不同的設計模式。MVCMVC是指Modle,Vi

Android開發MVP模式淺析

目前為止,MVP的使用還沒有一個標準,在此先記錄一下目前學習到的一些Android中使用MVP的知識。 按傳統的方式開發,經常會使Activity中混雜著UI互動,業務邏輯等流程。而MVP模式能巧妙的解決這個問題。先直接上一個小例子吧。 /** * 定義一個對UI元件

nginxlocation重要語法講解實踐檢驗

linuxnginx常見日誌收集及分析工具有rsyslog,awstats,flume,elk,storm等1 nginx location作用: location指令的作用是可以根據用戶請求的URL來執行不同的應用,就是根據用戶請求的網站地址URL匹配,匹配成功即進行相關的操作。2 loc

Android XListView實現原理講解分析

就是 指定 不同 true -h -name 修改 一個 部分 XListview是一個非常受歡迎的下拉刷新控件,但是已經停止維護了。之前寫過一篇XListview的使用介紹,用起來非常簡單,這兩天放假無聊,研究了下XListview的實現原理,學到了很多,今天分享給大家。

細數androidMVP的“七宗罪”

前言 我們都知道,MVP是在MVC的基礎上做了一次升級,相比MVC,MVP中P層與V層隔離,V層只負責UI,業務邏輯由抽象出來的P層負責,真正意義上的隔離View的細節和複雜性的模式....... 好了好了,上面是網上MVP“鼓吹”的基本套路。“欲想讓其滅亡,必先使其膨脹”今天,反其道而行之

23設計模式概括六種設計原則(一)

一、設計模式分類 總體來說模式依據目的可分為建立型模式(Creational)、結構型模式(Structural)、行為型模式(Behavioral)三種。 建立型模式:處理物件的建立。共5種:工廠方法模式(Factory Method)、抽象工廠模式(Abstract Factory)、建造者模式(Bu

Android夜間模式的三種實現方式

參考:https://www.jianshu.com/p/f3aaed57fa15 在本篇文章中給出了三種實現日間/夜間模式切換的方案: 使用 setTheme 的方法讓 Activity 重新設定主題; 設定 Android Support Library 中的 UiMode

Android GPU呈現模式原理卡頓掉幀淺析

APP開發中,卡頓絕對優化的大頭,Google為了幫助開發者更好的定位問題,提供了不少工具,如Systrace、GPU呈現模式分析工具、Android Studio自帶的CPU Profiler等,主要是輔助定位哪段程式碼、哪塊邏輯比較耗時,影響UI渲染,導致了卡頓。拿Profile GPU Renderin

Android設計模式-Builder模式

遇到多個構造器引數時要考慮使用構建器 引入:當我們建立物件傳遞引數的時候,往往通過構造方法來傳,如下程式碼: public class Person { private String name; private String id;

AndroidAPK安裝過程原理解析

應用安裝是智慧機的主要特點,即使用者可以把各種應用(如遊戲等)安裝到手機上,並可以對其進行解除安裝等管理操作。APK是Android Package的縮寫,即Android安裝包。APK是類似Symbian Sis或Sisx的檔案格式。通過將APK檔案直接傳到Android模擬器或Android手機中執行即可

Android開發MVP模式--專案實戰

1 前言 蘇寧+App是蘇寧易購集團零售雲研發中心分銷研發中心主要產品之一,由於專案處於初期階段,業務邏輯複雜,導致業務需求變動快,常常在開發甚至測試過程中出現介面或者後臺介面調整的情況。 App客戶端如何在外部需求不斷變化的情況下,降低模組耦合,儘可能減少

Android設計模式--狀態模式(將動作委託到當前狀態,狀態之間可以互相轉換)

   狀態模式:將狀態封裝成為獨立類,並將動作委託到當前狀態;狀態之間可以相互轉換,因為實現了相同的介面;狀態改變,則動作會跟著改變。  理解: 1.定義狀態介面,所有的狀態均實現該介面,這樣對於客戶(呼叫者)來說,狀態是可以替換的,客戶不關心具體的狀態是什麼,只是呼叫介

AndroidSqlite的使用效能優化

使用 可以使用安卓原生的工具類: SQLiteOpenHelper 抽象類:通過從此類繼承實現使用者類,來提供資料庫開啟、關閉等操作函式。 SQLiteDatabase 資料庫訪問類:執行對資料庫的插入記錄、查詢記錄等操作。 SQLiteCursor 查詢結構操作類:用來訪問查詢結果中的記錄

Android的快取處理非同步載入圖片類的封裝

一、快取介紹: (一)、Android中快取的必要性: 智慧手機的快取管理應用非常的普遍和需要,是提高使用者體驗的有效手段之一。 1、沒有快取的弊端: 流量開銷:對於客戶端——伺服器端應用,從遠端獲取圖片算是經常要用的一個功能,而圖片資源往往會消耗比較大的流量。 載入速