【轉】Android MVP架構搭建——mvp 重在思想

目錄

  • 引言
  • 爲何用MVP架構
  • MVP理論知識
  • 乞丐版MVP架構模式的代碼實現
  • MVP中的代碼複用場景
  • 平民版MVP架構 - base層頂級父類
  • Fragment怎麼辦
  • 時尚版MVP架構 - Model層的單獨優化

引言

記得第一次接觸MVP開發是上大學的時候,當時看了數十篇關於MVP的文章,這裏不得不吐槽一下國內技術帖子的質量真是參次不齊啊。看完以後一直懵懵懂懂的,總覺有幾處關鍵的地方沒搞清可是文章中卻一帶而過了,好比:html

  • 關於如何在Activity中高效的複用Presenter和View;
  • Mode層定義到什麼程度纔算是比較理想的解耦;
  • Model層與Presenter層如何比較優雅的相互通訊。

抱着這些問題,我本身摸索着構建出了一套個性化風格MVP架構,使用過程當中也優化了幾回,現在一年多過去了再看這套架構也就算是個能用吧,因此決定新的架構優化。小程序

本文講述了MVP的核心概念和如何從最初的乞丐版MVP架構一步步升級到平民版MVP架構,時尚版MVP架構,以及即將開始更新的旗艦版MVP架構,爲了保證思路清晰,文中包含大量代碼與文字,跟着文中的例子即可寫出一個完整的MVP架構。安全

爲何用MVP架構

其實咱們平常開發中的Activity,Fragment和XML界面就至關因而一個 MVC 的架構模式,Activity中不只要處理各類 UI 操做還要請求數據以及解析。網絡

這種開發方式的缺點就是業務量大的時候一個Activity 文件分分鐘飆到上千行代碼,想要改一處業務邏輯光是去找就要費半天勁,並且有點地方邏輯處理是同樣的無奈是不一樣的 Activity 就沒辦法很好的寫成通用方法。架構

那 MVP 爲啥好用呢?app

MVP 模式將Activity 中的業務邏輯所有分離出來,讓Activity 只作 UI 邏輯的處理,全部跟Android API無關的業務邏輯由 Presenter 層來完成。異步

將業務處理分離出來後最明顯的好處就是管理方便,可是缺點就是增長了代碼量。ide

MVP 理論知識

在MVP 架構中跟MVC相似的是一樣也分爲三層。post

Activity 和Fragment 視爲View層,負責處理 UI。優化

Presenter 爲業務處理層,既能調用UI邏輯,又能請求數據,該層爲純Java類,不涉及任何Android API。

Model 層中包含着具體的數據請求,數據源。

三層之間調用順序爲view->presenter->model,爲了調用安全着想不可反向調用!不可跨級調用!

那Model 層如何反饋給Presenter 層的呢?Presenter 又是如何操控View 層呢?看圖!

上圖中說明了低層的不會直接給上一層作反饋,而是經過 View 、 Callback 爲上級作出了反饋,這樣就解決了請求數據與更新界面的異步操做。上圖中 View 和 Callback 都是以接口的形式存在的,其中 View 是經典 MVP 架構中定義的,Callback 是我本身加的。

View 中定義了 Activity 的具體操做,主要是些將請求到的數據在界面中更新之類的。

Callback 中定義了請求數據時反饋的各類狀態:成功、失敗、異常等。

乞丐版MVP架構模式的代碼實現

下面咱們用 MVP 模式構造一個簡易模擬請求網絡的小程序。效果圖以下:

下面是Demo中的Java文件目錄:

Callback接口

Callback 接口是Model層給Presenter層反饋請求信息的傳遞載體,因此須要在Callback中定義數據請求的各類反饋狀態:

public interface MvpCallback {
   /**
     * 數據請求成功
     * @param data 請求到的數據
     */
    void onSuccess(String data);
    /**
     *  使用網絡API接口請求方式時,雖然已經請求成功可是由
     *  於{@code msg}的緣由沒法正常返回數據。
     */
    void onFailure(String msg);
     /**
     * 請求數據失敗,指在請求網絡API接口請求方式時,出現沒法聯網、
     * 缺乏權限,內存泄露等緣由致使沒法鏈接到請求數據源。
     */
    void onError();
    /**
     * 當請求數據結束時,不管請求結果是成功,失敗或是拋出異常都會執行此方法給用戶作處理,一般作網絡
     * 請求時能夠在此處隱藏「正在加載」的等待控件
     */
    void onComplete();
}

Model 類

Model 類中定了具體的網絡請求操做。爲模擬真實的網絡請求,利用postDelayed方法模擬耗時操做,經過判斷請求參數反饋不一樣的請求狀態:

public class MvpModel {
    /**
     * 獲取網絡接口數據
     * @param param 請求參數
     * @param callback 數據回調接口
     */
    public static void getNetData(final String param, final MvpCallback callback){
        // 利用postDelayed方法模擬網絡請求數據的耗時操做
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                switch (param){
                    case "normal":
                        callback.onSuccess("根據參數"+param+"的請求網絡數據成功");
                        break;
                    case "failure":
                        callback.onFailure("請求失敗:參數有誤");
                        break;
                    case "error":
                        callback.onError();
                        break;
                }
                callback.onComplete();
            }
        },2000);
    }
}

View 接口

View接口是Activity與Presenter層的中間層,它的做用是根據具體業務的須要,爲Presenter提供調用Activity中具體UI邏輯操做的方法。

public interface MvpView {
    /**
     * 顯示正在加載進度框
     */
    void showLoading();
    /**
     * 隱藏正在加載進度框
     */
    void hideLoading();
    /**
     * 當數據請求成功後,調用此接口顯示數據
     * @param data 數據源
     */
    void showData(String data);
    /**
     * 當數據請求失敗後,調用此接口提示
     * @param msg 失敗緣由
     */
    void showFailureMessage(String msg);
    /**
     * 當數據請求異常,調用此接口提示
     */
    void showErrorMessage();
}

Presenter類

Presenter類是具體的邏輯業務處理類,該類爲純Java類,不包含任何Android API,負責請求數據,並對數據請求的反饋進行處理。

Presenter類的構造方法中有一個View接口的參數,是爲了可以經過View接口通知Activity進行更新界面等操做。

public class MvpPresenter {
    // View接口
    private MvpView mView;
    public MvpPresenter(MvpView view){
        this.mView = view;
    }
    /**
     * 獲取網絡數據
     * @param params 參數
     */
    public void getData(String params){
        //顯示正在加載進度條
        mView.showLoading();
        // 調用Model請求數據
        MvpModel.getNetData(params, new MvpCallback() {
            @Override
            public void onSuccess(String data) {
                //調用view接口顯示數據
                mView.showData(data);
            }
            @Override
            public void onFailure(String msg) {
                //調用view接口提示失敗信息
                mView.showFailureMessage(msg);
            }
            @Override
            public void onError() {
                //調用view接口提示請求異常
                mView.showErrorMessage();
            }
            @Override
            public void onComplete() {
                // 隱藏正在加載進度條
                mView.hideLoading();
            }
        });
    }
}

Activity

在Activity代碼中須要強調的是若是想要調用Presenter就要先實現Presenter須要的對應的View接口。

public class MainActivity extends AppCompatActivity implements MvpView  {
    //進度條
    ProgressDialog progressDialog;
    TextView text;
    MvpPresenter presenter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        text = (TextView)findViewById(R.id.text);
        // 初始化進度條
        progressDialog = new ProgressDialog(this);
        progressDialog.setCancelable(false);
        progressDialog.setMessage("正在加載數據");
        //初始化Presenter
        presenter = new MvpPresenter(this);
    }
    // button 點擊事件調用方法
    public void getData(View view){
        presenter.getData("normal");
    }
    // button 點擊事件調用方法
    public void getDataForFailure(View view){
        presenter.getData("failure");
    }
    // button 點擊事件調用方法
    public void getDataForError(View view){
        presenter.getData("error");
    }
    @Override
    public void showLoading() {
        if (!progressDialog.isShowing()) {
            progressDialog.show();
        }
    }
    @Override
    public void hideLoading() {
        if (progressDialog.isShowing()) {
            progressDialog.dismiss();
        }
    }
    @Override
    public void showData(String data) {
        text.setText(data);
    }
    @Override
    public void showFailureMessage(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
        text.setText(msg);
    }
    @Override
    public void showErrorMessage() {
        Toast.makeText(this, "網絡請求數據出現異常", Toast.LENGTH_SHORT).show();
        text.setText("網絡請求數據出現異常");
    }
}

至此,已經完整的實現了一個簡易的MVP架構。

注意!以上代碼中還存在很大的問題,不能用到實際開發中,下面會討論目前存在的問題以及如何優化。

MVP中的代碼複用場景

由於上節中乞丐版MVP Demo的代碼只實現了一個Activity的請求操做,容易出現一個概念的混淆:

每一個Activity都須要有與它對應的一套MVP(Model,View,Presenter)嗎?

答案確定是否認的!

首先不須要數據請求的Activity固然就一樣不須要MVP輔助。與其餘Activity中存在相同邏輯的Activity,就不須要重複生成對應的MVP。可是這個存在相同邏輯的定義,不一樣的場景有不一樣的說法:

場景1:業務邏輯徹底相同

場景1中Activity A和Activity C都只有一個「買東西」的邏輯,屬於典型的邏輯相同,因此Activity C就能夠直接用Activity A寫好的MVP無需再作任何處理。

場景二、3:包含部分相同業務邏輯

場景2和場景3的邏輯相似,都屬於一個業務邏輯中包含另一個能夠單獨存在的業務邏輯,這種狀況採用繼承的方法便可

場景4

場景4中Activity C想要同時調用獨立服務於Activity A 和 Activity B的業務邏輯,只須要將兩個業務邏輯對應的Presenter分別實例化並調用業務方法便可:

總結

經過上面一攬子場景的分析,得出的第一個結論就是MVP的結構太過於繁重,因此爲了不多寫重複代碼和往後須要進行無心義的修改,在開發前必定要設計好邏輯調用圖,這樣才能事半功倍。

對於上面經典的經過業務邏輯繼承實現包含重複邏輯的方法,其實也能夠在一個Presenter中寫好完整的邏輯方法,對於不一樣的Activity須要哪一個業務邏輯方法就調用哪一個,這樣豈不就簡單多了。可是從架構設計角度看這種作法是不嚴謹的,可能存在漏洞,因此爲保持軟件架構的健壯仍是不要偷懶的好。

平民版MVP架構 - base層頂級父類

以前說過乞丐版MVP架構模式中還存在不少問題不能應用到實際的開發中,大概存在的問題有:

  • 構架存在漏洞
  • 代碼冗餘量大
  • 通用性差

針對這些問題咱們須要進一步優化,單車變摩托,升級爲能夠在實際開發中使用的平民版MVP架構。

平民版MVP架構 優化代碼可參考 ————>>>>

博文出處:http://www.jcodecraeer.com/a/anzhuokaifa/2017/1020/8625.html?1508484926

相關文章
相關標籤/搜索