MVP架構由淺入深篇一(基礎版)

居家小男發表於2020-09-25

MVP架構由淺入深篇一

前言:不得不說,才開始學習MVP架構各種介面的呼叫和解耦程式碼真的讓人眼花繚亂,對於很多問題都是一知半解:

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

所以決定靜下心來理解一下該架構的藝術。部落格主要參考https://www.jianshu.com/p/5c3bc32afa36?utm_campaign=haruki,其中加入自己的理解和程式碼實現來學習和驗證MVP架構的使用技巧。


目錄

MVP架構由淺入深篇一

一、MVP架構概述

1.為什麼要使用MVP架構?

2.MVP理論知識

二、基礎版MVP程式碼實現

1.檔案結構

2.Callback介面

3.Model 類

4.View 介面

5.Presenter類

6.佈局檔案

7.MainActivity

8.執行結果:(以成功請求資料為例)

三、基礎版MVP問題分析

場景1:業務邏輯完全相同

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

場景4

場景5

基礎版MVP總結


 

一、MVP架構概述

1.為什麼要使用MVP架構?

MVP(Model、View、Presenter)模式將Activity中的業務邏輯分離出來,避免了Activity邏輯的高耦合,可以把Model理解成房東,View就是找房的自己,Presenter就是中介,讓Activity只做UI的功能,把其他功能抽出去讓其他層來完成,缺點就是程式碼量增加了,但是帶來的優點遠超缺點,尤其是專案比較大的時候更是事半功倍。

2.MVP理論知識

在MVP架構中跟MVC類似分為三層。

  1. Activity和Fragment視為View層   -->   理解為四處找房的自己
  2. Presenter為業務處理層,既能呼叫UI邏輯,又能請求資料   -->   相當於中介公司
  3. Model層中包含著具體的資料請求,資料來源   -->   相當於房東

三層之間呼叫順序為view-->presenter-->model,不能反向呼叫和跨級呼叫

由上圖可知,Model層通過Callback反饋資料給Presenter層,Presenter層通過View反饋操作給Activity層。其中View和Callback都是以介面的形式存在。

  • Callback中定義了請求資料時反饋的各種狀態:成功、失敗、錯誤等
  • View中定義了Activity的具體操作,主要是將請求得到的資料在介面上更新

二、基礎版MVP程式碼實現

因為是模擬網路資料請求,所以有三個請求資料的按鈕分別對應成功、失敗、異常三種不同的反饋狀態。

1.檔案結構

2.Callback介面

因為Callback介面是presenter和model打交道的介面,所以介面中定義了請求資料的方法

package com.example.mvpapplication;

/**
 * callback負責presenter和model互動
 * 相當於:中介和房東互動介面
 */
public interface MvpCallback {
    /**
     * 請求資料成功
     * @param data
     */
    void onSuccess(String data);

    /**
     * 使用網路API介面請求,請求資料失敗
     * @param msg
     */
    void onFailure(String msg);

    /**
     * 請求資料時發生錯誤
     */
    void onError();

    /**
     * 請求資料完成
     */
    void onComplete();
}

3.Model 類

Model 類中定了具體的網路請求操作。利用postDelayed方法模擬耗時操作,通過判斷請求引數反饋不同的請求狀態:(房東)

package com.example.mvpapplication;

import android.os.Handler;

public class MvpModel {
    /**
     * 獲取網路資料
     * @param param  請求引數
     * @param callback  資料回撥介面 -- 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);
    }
}

4.View 介面

View介面是Activity與Presenter層的中間層,它的作用是根據具體業務的需要,為Presenter提供呼叫Activity中具體UI邏輯操作的方法。相當於我和中介之間的交易

package com.example.mvpapplication;

/**
 * 這裡相當於找房子的自己--對UI進行操作的介面
 */
public interface MvpView {
    /**
     * 顯示正在載入資料進度
     */
    void showLoading();

    /**
     * 隱藏進度
     */
    void hideLoading();

    /**
     * 展示資料
     * @param data
     */
    void showData(String data);

    /**
     * 顯示資料錯誤原因回撥介面
     * @param msg
     */
    void showFailureMessage(String msg);

    /**
     * 當資料錯誤時回撥介面
     */
    void showErrorMessage();
}

5.Presenter類

Presenter類是具體的邏輯業務處理類,負責請求資料,並對資料請求的反饋進行處理。Presenter相當於中介的作用,所以裡面要有MvpView和Callback的宣告。

package com.example.mvpapplication;

/**
 * 這裡相當於中介的作用
 */
public class MvpPresenter {
    //因為中介要和僱主打交道,所以要宣告MvpView
    private MvpView mView;
    public MvpPresenter(MvpView view){
        this.mView = view;
    }

    /**
     * 相當於幫我找房子--請求資料
     * @param param
     */
    public void getData(String param){
        //顯示正在載入進度條
        mView.showLoading();
        //呼叫Model請求資料
        MvpModel.getNetData(param, new MvpCallback() /*開始和Model打交道的介面*/{
            @Override
            public void onSuccess(String data) {
                //呼叫view介面顯示資料
                mView.showData(data);
            }

            @Override
            public void onFailure(String msg) {
                mView.showFailureMessage(msg);
            }

            @Override
            public void onError() {
                mView.showErrorMessage();
            }

            @Override
            public void onComplete() {
                mView.hideLoading();
            }
        });
    }
}

6.佈局檔案

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical">

    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:text="點選按鈕獲取網路資料"/>


    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="獲取資料【成功】"
        android:onClick="getData"
        />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="獲取資料【失敗】"
        android:onClick="getDataForFailure"
        />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="獲取資料【異常】"
        android:onClick="getDataForError"
        />

</LinearLayout>

7.MainActivity

在MainActivity中,主要是對MvpView的實現,進行UI操作

package com.example.mvpapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.app.ProgressDialog;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

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 = 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");
    }
    public void getDataForFailure(View view){
        presenter.getData("failure");
    }
    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("請求異常");
    }
}

8.執行結果:(以成功請求資料為例)

至此,基礎版MVP已經完成了,下面我們來分析一下基礎版存在的問題和如何優化?

三、基礎版MVP問題分析

通過上面的程式碼實現,看似已經完成了MVP的框架了,但是遠遠不夠,其中一個問題就是複用問題。

由於上述例子我們只有一個業務邏輯,即只有一個Activity,那如果有兩個或者更多Activity呢?我們需要建立N多個MVP框架嗎?當然在不需要MVP的Activity中當然不用建立,可是當需要MVP的Activity呢?當然這也要分情況,如果Activity業務邏輯相似當然可以複用,但是當業務邏輯不一樣時,就必須建立另一個MVP了。可是又如何複用業務邏輯相似的MVP呢?請看下述分析:

場景1:業務邏輯完全相同

這種場景很簡單,由於業務邏輯完全相同,就直接複用就好了。

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

場景2和場景3的邏輯類似,都屬於一個業務邏輯中包含另外一個可以單獨存在的業務邏輯,這種情況採用繼承的方法即可。

場景4

場景4中Activity C想要同時呼叫獨立服務於Activity A 和 Activity B的業務邏輯,只需要將兩個業務邏輯對應的Presenter分別例項化並呼叫業務方法即可。

場景5

場景5屬於場景3與場景4的結合體,同樣需要先把A和B的業務邏輯拆分開,然後同時呼叫。

基礎版MVP總結

從上述的分析可知,基礎版MVP存在複用問題。由於MVP框架還算是比較繁重的,所以複用很有必要,可以減少很多沒必要的重複程式碼。我們能想到的方法就是類比Binder連線池,在Presenter層寫完整的業務邏輯,然後通過不同的業務標誌進行不同的業務處理,這種方式很簡單,但是這樣的話,可能會有一定的問題,比如Presenter層業務過於繁重,維護較難等。畢竟架構師提出的複用方案是實踐得出的真理,存在即是合理的。在下一篇中,我們將介紹進階版MVP框架的實現。

相關文章