天天看點

Android系統架構之MVP使用

Android系統架構之MVP使用

在開始這篇部落格之前,我們先來探讨幾個問題:

  1. 為什麼要用MVP?

    傳統開發中,View(包括Activity、Fragment及其子類)作為APP功能的基本單元完成了過多任務,包括UI繪制與重新整理、頁面邏輯、動畫渲染,這就導緻一些複雜的頁面動辄數千行,這就導緻維護和更新的成本越來越高,代碼牽一發而動全身。為了解決這個問題,我們提出了MVP的思想。

  2. MVP如何解決這個View代碼過于龐大的問題?

    MVP全稱是Model,View,Presenter,核心思想就是将整個應用分為三層。

  3. View層:視圖層,包含界面相關功能,主要提供與使用者的互動,包括實作設計師要求的界面以及動畫的加載等互動效果。View層一般會持有Presenter的引用,通過這個引用将一些業務邏輯委托給Presenter完成,或者也可以通過依賴注入(如Dagger)的方式獲得Presenter的執行個體進而實作邏輯操作的委托;
  4. Presenter層:邏輯控制層,這層是從View層中通過抽離控制邏輯部分形成的,充當View層和Model層互動的中間人,比如使用者在螢幕上點選了下載下傳按鈕,下載下傳一張圖檔并顯示出來,這時,View層會發出網絡請求,而Presenter層會接受View層的請求,并分發給對應的Model處理,最終結果通過Presenter層回報給View層并顯示出來;
  5. Model層:封裝各種資料來源,例如遠端網絡資料,本地資料庫資料,對Presenter層提供簡單的接口。
  6. MVP與MVC的差別

    MVP作為經典MVC的延伸,與MVC最大的差別在于:MVP中的View層和Model層沒有直接通信,是通過Presenter這個中間人進行的,其互動都是基于接口的,并且通常情況下,Presenter和View是一對一的,特殊情況下可能存在一個View對應多個Presenter的情況;而MVC中View和Model是直接通信的,Control本身是基于行為的,可以被多個View共享,比如網絡下載下傳行為。

OK,現在我們正式開始實作我們自己的MVP架構。

一、 建立callback接口,Callback 接口是Model層給Presenter層回報請求資訊的傳遞載體,是以需要在Callback中定義資料請求的各種回報狀态:

public interface MvpCallback {

/**
     * 資料請求成功
     * @param data 請求到的資料
     */
    void onSuccess(String data);
    /**
     *  使用網絡API接口請求方式時,雖然已經請求成功但是由于某些原因無法正常傳回資料。
     * @param 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();
            }
        });
    }           

五、 頁面布局,隻需要包括三個按鈕即可,就是對應于通路成功,失敗,異常三種情況的按鈕

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:orientation="vertical"
    tools:context="com.jessewu.mvpdemo.MainActivity">
    <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"
        />           

六、建立MainActivity,通過按鈕點選進行調用

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("網絡請求資料出現異常");
    }
}
           

分析一下代碼,這裡的View對應的就是我代碼中的MainActivity,Model對應我代碼中的MvpModel類,Presenter對應我代碼中的MvpPresenter類。代碼原理就是,在MainActivity中持有一個MvpPresenter類的執行個體presenter,使用者與MainActivity進行互動(準确的說應該是與MainActivity對應的頁面互動),使用者的網絡請求将會委托給presenter中的getData()方法,而在getData()中再将網絡資料請求操作委托給MvpModel中的靜态方法getNetData()進行實作,因為是公有靜态,是以這裡不需要持有Model的引用,同時通過View接口實作對頁面的更新,這個View接口是在MainActivity中實作的。

從整體上看,MVP其實首先就是通過面向對象技術将一個頁面及其完整的互動邏輯封裝為三部分,分别是Model, View和Presenter, 三者之間通過接口進行互動,Model與Present二者完全解耦,二者通過Presenter實作互動,而在Presenter中在通過一種多态實作頁面重新整理接口中方法,這個方法的實作在View中。

作者:kuai_j

來源:CSDN

原文:

https://blog.csdn.net/qq_41613281/article/details/90449133

版權聲明:本文為部落客原創文章,轉載請附上博文連結!