Android MVP架構從入門到精通-真槍實彈

一. 前言

你是否遇到過Activity/Fragment中成百上千行代碼,徹底沒法維護,看着頭疼?android

你是否遇到過因後臺接口還未寫而你不能先寫代碼邏輯的狀況?git

你是否遇到過用MVC架構寫的項目進行單元測試時的深深無奈?github

若是你如今仍是用MVC架構模式在寫項目,請先轉到MVP模式!數據庫

二. MVC架構

MVC架構模式最初生根於服務器端的Web開發,後來漸漸可以勝任客戶端Web開發,再後來因Android項目由XML和Activity/Fragment組成,慢慢的Android開發者開始使用相似MVC的架構模式開發應用.api

mvc架構模式

M層:模型層(model),主要是實體類,數據庫,網絡等存在的層面,model將新的數據發送到view層,用戶獲得數據響應.bash

V層:視圖層(view),通常指XML爲表明的視圖界面.顯示來源於model層的數據.用戶的點擊操做等事件從view層傳遞到controller層.服務器

C層:控制層(controller),通常以Activity/Fragment爲表明.C層主要是鏈接V層和M層的,C層收到V層發送過來的事件請求,從M層獲取數據,展現給V層.網絡

從上圖能夠看出M層和V層有鏈接關係,而Activity有時候既充當了控制層又充當了視圖層,致使項目維護比較麻煩.架構

1. MVC架構優缺點
A. 缺點
  1. M層和V層有鏈接關係,沒有解耦,致使維護困難.mvc

  2. Activity/Fragment中的代碼過多,難以維護.

Activity中有不少關於視圖UI的顯示代碼,所以View視圖和Activity控制器並非徹底分離的,當Activity類業務過多的時候,會變得難以管理和維護.尤爲是當UI的狀態數據,跟持久化的數據混雜在一塊兒,變得極爲混亂.

B. 優勢
  1. 控制層和View層都在Activity中進行操做,數據操做方便.

  2. 模塊職責劃分明確.主要劃分層M,V,C三個模塊.

三. MVP架構

MVP

MVP,便是Model,View,Presenter架構模式.看起來相似MVC,其實否則.從上圖能看到Model層和View層沒有相鏈接,徹底解耦.

用戶觸碰界面觸發事件,View層把事件通知Presenter層,Presenter層通知Model層處理這個事件,Model層處理後把結果發送到Presenter層,Presenter層再通知View層,最後View層作出改變.這是一整套流程.

M層:模型層(Model),此層和MVC中的M層做用相似.

V層:視圖層(View),在MVC中V層只包含XML文件,而MVP中V層包含XML,Activity和Fragment三者.理論上V層不涉及任何邏輯,只負責界面的改變,儘可能把邏輯處理放到M層.

P層:通知層(Presenter),P層的主要做用就是鏈接V層和M層,起到一個通知傳遞數據的做用.

1. MVP架構優缺點
A. 缺點
  1. MVP中接口過多.

  2. 每個功能,相比於MVC要多寫好幾個文件.

  3. 若是某一個界面中須要請求多個服務器接口,這個界面文件中會實現不少的回調接口,致使代碼繁雜.

  4. 若是更改了數據源和請求中參數,會致使更多的代碼修改.

  5. 額外的代碼複雜度及學習成本.

B. 優勢
  1. 模塊職責劃分明顯,層次清晰,接口功能清晰.

  2. Model層和View層分離,解耦.修改View而不影響Model.

  3. 功能複用度高,方便.一個Presenter能夠複用於多個View,而不用更改Presenter的邏輯.

  4. 有利於測試驅動開發,之前的Android開發是難以進行單元測試.

  5. 若是後臺接口還未寫好,但已知返回數據類型的狀況下,徹底能夠寫出此接口完整的功能.

四. MVP架構實戰(真槍實彈)

1. MVP三層代碼簡單書寫

接下來筆者從簡到繁,一點一點的堆砌MVP的整個架構.先看一下XML佈局,佈局中一個Button按鈕和一個TextView控件,用戶點擊按鈕後,Presenter層通知Model層請求處理網絡數據,處理後Model層把結果數據發送給Presenter層,Presenter層再通知View層,而後View層改變TextView顯示的內容.

MVP

<?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:layout_gravity="center"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".view.SingleInterfaceActivity">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="點擊" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="100px"
        android:text="請點擊上方按鈕獲取數據" />
</LinearLayout>
複製代碼

接下來是Activity代碼,裏面就是獲取Button和TextView控件,而後對Button作監聽,先簡單的這樣寫,一會慢慢的增長代碼.

public class SingleInterfaceActivity extends AppCompatActivity {

    private Button button;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_single_interface);
        button = findViewById(R.id.button);
        textView = findViewById(R.id.textView);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });

    }
}
複製代碼

下面是Model層代碼.本次網絡請求用的是wanandroid網站的開放api,其中的文章首頁列表接口.SingleInterfaceModel文件裏面有一個方法getData,第一個參數curPage意思是獲取第幾頁的數據,第二個參數callback是Model層通知Presenter層的回調.

public class SingleInterfaceModel {
    
    public void getData(int curPage, final Callback callback) {
        NetUtils.getRetrofit()
                .create(Api.class)
                .getData(curPage)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<ArticleListBean>() {
                    @Override
                    public void onCompleted() {
                        LP.w("completed");
                    }

                    @Override
                    public void onError(Throwable e) {
                        callback.onFail("出現錯誤");
                    }

                    @Override
                    public void onNext(ArticleListBean bean) {
                        if (null == bean) {
                            callback.onFail("出現錯誤");
                        } else if (bean.errorCode != 0) {
                            callback.onFail(bean.errorMsg);
                        } else {
                            callback.onSuccess(bean);
                        }
                    }
                });
    }
}
複製代碼

Callback文件內容以下.裏面一個成功一個失敗的回調接口,參數全是泛型,爲啥使用泛型筆者就不用說了吧.

public interface Callback<K, V> {
    void onSuccess(K data);

    void onFail(V data);
}
複製代碼

再接下來是Presenter層的代碼.SingleInterfacePresenter類構造函數中直接new了一個Model層對象,用於Presenter層對Model層的調用.而後SingleInterfacePresenter類的方法getData用於與Model的互相鏈接.

public class SingleInterfacePresenter {
    private final SingleInterfaceModel singleInterfaceModel;

    public SingleInterfacePresenter() {
        this.singleInterfaceModel = new SingleInterfaceModel();
    }

    public void getData(int curPage) {
        singleInterfaceModel.getData(curPage, new Callback<ArticleListBean, String>() {
            @Override
            public void onSuccess(ArticleListBean loginResultBean) {
                //若是Model層請求數據成功,則此處應執行通知View層的代碼
                
            }

            @Override
            public void onFail(String errorMsg) {
                //若是Model層請求數據失敗,則此處應執行通知View層的代碼
                
            }
        });
    }
}
複製代碼

至此,MVP三層簡單的部分代碼算是完成.那麼怎樣進行整個流程的相互調用呢.咱們把剛開始的SingleInterfaceActivity代碼改一下,讓SingleInterfaceActivity持有Presenter層的對象,這樣View層就能夠調用Presenter層了.修改後代碼以下.

public class SingleInterfaceActivity extends AppCompatActivity {

    private Button button;
    private TextView textView;
    private SingleInterfacePresenter singleInterfacePresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_single_interface);
        button = findViewById(R.id.button);
        textView = findViewById(R.id.textView);

        singleInterfacePresenter = new SingleInterfacePresenter();
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                singleInterfacePresenter.getData(0);
            }
        });

    }
}
複製代碼

從以上全部代碼能夠看出,當用戶點擊按鈕後,View層按鈕的監聽事件執行調用了Presenter層對象的getData方法,此時,Presenter層對象的getData方法調用了Model層對象的getData方法,Model層對象的getData方法中執行了網絡請求和邏輯處理,把成功或失敗的結果經過Callback接口回調給了Presenter層,而後Presenter層再通知View層改變界面.但此時SingleInterfacePresenter類中收到Model層的結果後沒法通知View層,由於SingleInterfacePresenter未持有View層的對象.以下代碼的註釋中有說明.(若是此時點擊按鈕,下方代碼LP.w()處會打印出網絡請求成功的log)

public class SingleInterfacePresenter {
    private final SingleInterfaceModel singleInterfaceModel;

    public SingleInterfacePresenter() {
        this.singleInterfaceModel = new SingleInterfaceModel();
    }

    public void getData(int curPage) {
        singleInterfaceModel.getData(curPage, new Callback<ArticleListBean, String>() {
            @Override
            public void onSuccess(ArticleListBean loginResultBean) {
                //若是Model層請求數據成功,則此處應執行通知View層的代碼
                //LP.w()是一個簡單的log打印
                LP.w(loginResultBean.toString());
            }

            @Override
            public void onFail(String errorMsg) {
                //若是Model層請求數據失敗,則此處應執行通知View層的代碼

            }
        });
    }
}
複製代碼

代碼寫到這裏,筆者先把這些代碼提交到github(github.com/serge66/MVP…),github上會有一次提交記錄,若是想看此時的代碼,能夠根據提交記錄"第一次修改"克隆此時的代碼.

2. P層V層溝通橋樑

如今P層未持有V層對象,不能通知V層改變界面,那麼就繼續演變MVP架構. 在MVP架構中,咱們要爲每一個Activity/Fragment寫一個接口,這個接口須要讓Presenter層持有,P層經過這個接口去通知V層更改界面.接口中包含了成功和失敗的回調,這個接口Activity/Fragment要去實現,最終P層才能通知V層.

public interface SingleInterfaceIView {
    void showArticleSuccess(ArticleListBean bean);

    void showArticleFail(String errorMsg);
}
複製代碼

一個完整的項目之後確定會有許多功能界面,那麼咱們應該抽出一個IView公共接口,讓全部的Activity/Fragment都間接實現它.IVew公共接口是用於給View層的接口繼承的,注意,不是View自己繼承.由於它定義的是接口的規範, 而其餘接口才是定義的類的規範(這句話請仔細理解).

/**
 * @Description: 公共接口 是用於給View的接口繼承的,注意,不是View自己繼承。
 * 					由於它定義的是接口的規範, 而其餘接口才是定義的類的規範
 * @Author: lishengjiejob@163.com
 * @Time: 2018/11/22 17:26
 */
public interface IView {
}
複製代碼

這個接口中能夠寫一些全部Activigy/Fragment共用的方法,咱們把SingleInterfaceIView繼承IView接口.

public interface SingleInterfaceIView extends IView {
    void showArticleSuccess(ArticleListBean bean);

    void showArticleFail(String errorMsg);
}
複製代碼

同理Model層和Presenter層也是如此.

public interface IModel {
}
複製代碼
public interface IPresenter {
}
複製代碼

如今項目中Model層是一個SingleInterfaceModel類,這個類對象被P層持有,對於面向對象設計來說,利用接口達到解耦目的已經人盡皆知,那咱們就要對SingleInterfaceModel類再寫一個可繼承的接口.代碼以下.

public interface ISingleInterfaceModel extends IModel {
    void getData(int curPage, final Callback callback);
}
複製代碼

如此,SingleInterfaceModel類的修改以下.

public class SingleInterfaceModel implements ISingleInterfaceModel {

    @Override
    public void getData(int curPage, final Callback callback) {
        NetUtils.getRetrofit()
                .create(Api.class)
                .getData(curPage)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<ArticleListBean>() {
                    @Override
                    public void onCompleted() {
                        LP.w("completed");
                    }

                    @Override
                    public void onError(Throwable e) {
                        callback.onFail("出現錯誤");
                    }

                    @Override
                    public void onNext(ArticleListBean bean) {
                        if (null == bean) {
                            callback.onFail("出現錯誤");
                        } else if (bean.errorCode != 0) {
                            callback.onFail(bean.errorMsg);
                        } else {
                            callback.onSuccess(bean);
                        }
                    }
                });
    }
}
複製代碼

同理,View層持有P層對象,咱們也須要對P層進行改造.可是下面的代碼卻沒有像ISingleInterfaceModel接口繼承IModel同樣繼承IPresenter,這點須要注意,筆者把IPresenter的繼承放在了其餘處,後面會講解.

public interface ISingleInterfacePresenter {
    void getData(int curPage);
}

複製代碼

而後SingleInterfacePresenter類的修改以下:

public class SingleInterfacePresenter implements ISingleInterfacePresenter {
    private final ISingleInterfaceModel singleInterfaceModel;

    public SingleInterfacePresenter() {
        this.singleInterfaceModel = new SingleInterfaceModel();
    }

    @Override
    public void getData(int curPage) {
        singleInterfaceModel.getData(curPage, new Callback<ArticleListBean, String>() {
            @Override
            public void onSuccess(ArticleListBean loginResultBean) {
                //若是Model層請求數據成功,則此處應執行通知View層的代碼
                //LP.w()是一個簡單的log打印
                LP.w(loginResultBean.toString());
            }

            @Override
            public void onFail(String errorMsg) {
                //若是Model層請求數據失敗,則此處應執行通知View層的代碼
                LP.w(errorMsg);
            }
        });
    }
}
複製代碼
3. 生命週期適配

至此,MVP三層每層的接口都寫好了.可是P層鏈接V層的橋樑尚未搭建好,這個慢慢來,一個好的高樓大廈都是一步一步建造的.上面IPresenter接口咱們沒有讓其餘類繼承,接下來就講下這個.P層和V層相鏈接,V層的生命週期也要適配到P層,P層的每一個功能都要適配生命週期,這裏能夠把生命週期的適配放在IPresenter接口中.P層持有V層對象,這裏把它放到泛型中.代碼以下.

public interface IPresenter<T extends IView> {

    /**
     * 依附生命view
     *
     * @param view
     */
    void attachView(T view);

    /**
     * 分離View
     */
    void detachView();

    /**
     * 判斷View是否已經銷燬
     *
     * @return
     */
    boolean isViewAttached();
    
}
複製代碼

這個IPresenter接口須要全部的P層實現類繼承,對於生命週期這部分功能都是通用的,那麼就能夠抽出來一個抽象基類BasePresenter,去實現IPresenter的接口.

public abstract class BasePresenter<T extends IView> implements IPresenter<T> {
    protected T mView;

    @Override
    public void attachView(T view) {
        mView = view;
    }

    @Override
    public void detachView() {
        mView = null;
    }

    @Override
    public boolean isViewAttached() {
        return mView != null;
    }
}

複製代碼

此時,SingleInterfacePresenter類的代碼修改以下.泛型中的SingleInterfaceIView能夠理解成對應的Activity,P層此時完成了對V層的通訊.

public class SingleInterfacePresenter extends BasePresenter<SingleInterfaceIView> implements ISingleInterfacePresenter {
    private final ISingleInterfaceModel singleInterfaceModel;

    public SingleInterfacePresenter() {
        this.singleInterfaceModel = new SingleInterfaceModel();
    }

    @Override
    public void getData(int curPage) {
        singleInterfaceModel.getData(curPage, new Callback<ArticleListBean, String>() {
            @Override
            public void onSuccess(ArticleListBean loginResultBean) {
                //若是Model層請求數據成功,則此處應執行通知View層的代碼
                //LP.w()是一個簡單的log打印
                LP.w(loginResultBean.toString());
                if (isViewAttached()) {
                    mView.showArticleSuccess(loginResultBean);
                }
            }

            @Override
            public void onFail(String errorMsg) {
                //若是Model層請求數據失敗,則此處應執行通知View層的代碼
                LP.w(errorMsg);
                if (isViewAttached()) {
                    mView.showArticleFail(errorMsg);
                }
            }
        });
    }
}
複製代碼

此時,P層和V層的鏈接橋樑已經搭建,但還未搭建完成,咱們須要寫個BaseMVPActvity讓全部的Activity繼承,統一處理Activity相同邏輯.在BaseMVPActvity中使用IPresenter的泛型,由於每一個Activity中須要持有P層對象,這裏把P層對象抽出來也放在BaseMVPActvity中.同時BaseMVPActvity中也須要繼承IView,用於P層對V層的生命週期中.代碼以下.

public abstract class BaseMVPActivity<T extends IPresenter> extends AppCompatActivity implements IView {

    protected T mPresenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initPresenter();
        init();
    }

    protected void initPresenter() {
        mPresenter = createPresenter();
        //綁定生命週期
        if (mPresenter != null) {
            mPresenter.attachView(this);
        }
    }

    @Override
    protected void onDestroy() {
        if (mPresenter != null) {
            mPresenter.detachView();
        }
        super.onDestroy();
    }

    /**
     * 建立一個Presenter
     *
     * @return
     */
    protected abstract T createPresenter();

    protected abstract void init();

}
複製代碼

接下來讓SingleInterfaceActivity實現這個BaseMVPActivity.

public class SingleInterfaceActivity extends BaseMVPActivity<SingleInterfacePresenter> implements SingleInterfaceIView {

    private Button button;
    private TextView textView;

    @Override
    protected void init() {
        setContentView(R.layout.activity_single_interface);
        button = findViewById(R.id.button);
        textView = findViewById(R.id.textView);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mPresenter.getData(0);
            }
        });
    }

    @Override
    protected SingleInterfacePresenter createPresenter() {
        return new SingleInterfacePresenter();
    }


    @Override
    public void showArticleSuccess(ArticleListBean bean) {
        textView.setText(bean.data.datas.get(0).title);
    }

    @Override
    public void showArticleFail(String errorMsg) {
        Toast.makeText(this, errorMsg, Toast.LENGTH_SHORT).show();
    }
}

複製代碼

到此,MVP架構的整個簡易流程完成.

代碼寫到這裏,筆者先把這些代碼提交到github(github.com/serge66/MVP…),github上會有一次提交記錄,若是想看此時的代碼,能夠根據提交記錄"第二次修改"克隆此時的代碼.

4. 優化MVP架構

MVP目錄

上面是MVP的目錄,從目錄中咱們能夠看到一個功能點(網絡請求)MVP三層各有兩個文件須要寫,相對於MVC來講寫起來確實麻煩,這也是一些人不肯意寫MVP,寧願用MVC的緣由.

這裏咱們能夠對此優化一下.MVP架構中有個Contract的概念,Contract有統一管理接口的做用,目的是爲了統一管理一個頁面的View和Presenter接口,用Contract能夠減小部分文件的建立,好比P層和V層的接口文件.

那咱們就把P層的ISingleInterfacePresenter接口和V層的SingleInterfaceIView接口文件刪除掉,放入SingleInterfaceContract文件中.代碼以下.

public interface SingleInterfaceContract {


    interface View extends IView {
        void showArticleSuccess(ArticleListBean bean);

        void showArticleFail(String errorMsg);
    }

    interface Presenter {
        void getData(int curPage);
    }


}
複製代碼

此時,SingleInterfacePresenter和SingleInterfaceActivity的代碼修改以下.

public class SingleInterfacePresenter extends BasePresenter<SingleInterfaceContract.View>
        implements SingleInterfaceContract.Presenter {

    private final ISingleInterfaceModel singleInterfaceModel;

    public SingleInterfacePresenter() {
        this.singleInterfaceModel = new SingleInterfaceModel();
    }

    @Override
    public void getData(int curPage) {
        singleInterfaceModel.getData(curPage, new Callback<ArticleListBean, String>() {
            @Override
            public void onSuccess(ArticleListBean loginResultBean) {
                //若是Model層請求數據成功,則此處應執行通知View層的代碼
                //LP.w()是一個簡單的log打印
                LP.w(loginResultBean.toString());
                if (isViewAttached()) {
                    mView.showArticleSuccess(loginResultBean);
                }
            }

            @Override
            public void onFail(String errorMsg) {
                //若是Model層請求數據失敗,則此處應執行通知View層的代碼
                LP.w(errorMsg);
                if (isViewAttached()) {
                    mView.showArticleFail(errorMsg);
                }
            }
        });
    }
}
複製代碼
public class SingleInterfaceActivity extends BaseMVPActivity<SingleInterfacePresenter>
        implements SingleInterfaceContract.View {

    private Button button;
    private TextView textView;

    @Override
    protected void init() {
        setContentView(R.layout.activity_single_interface);
        button = findViewById(R.id.button);
        textView = findViewById(R.id.textView);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mPresenter.getData(0);
            }
        });
    }

    @Override
    protected SingleInterfacePresenter createPresenter() {
        return new SingleInterfacePresenter();
    }


    @Override
    public void showArticleSuccess(ArticleListBean bean) {
        textView.setText(bean.data.datas.get(0).title);
    }

    @Override
    public void showArticleFail(String errorMsg) {
        Toast.makeText(this, errorMsg, Toast.LENGTH_SHORT).show();
    }
}

複製代碼

代碼寫到這裏,筆者先把這些代碼提交到github(github.com/serge66/MVP…),github上會有一次提交記錄,若是想看此時的代碼,能夠根據提交記錄"第三次修改"克隆此時的代碼.

5. 單頁面多網絡請求

上面的MVP封裝只適用於單頁面一個網絡請求的狀況,當一個界面有兩個網絡請求時,此封裝已不適合.爲此,咱們再次新建一個MultipleInterfaceActivity來進行說明.XML中佈局是兩個按鈕兩個Textview,點擊則能夠進行網絡請求.

MVP

<?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:layout_gravity="center"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".view.MultipleInterfaceActivity">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="點擊" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="50px"
        android:text="請點擊上方按鈕獲取數據" />

    <Button
        android:id="@+id/btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="100px"
        android:text="點擊" />

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="50px"
        android:text="請點擊上方按鈕獲取數據" />
</LinearLayout>
複製代碼

MultipleInterfaceActivity類代碼暫時以下.

public class MultipleInterfaceActivity extends BaseMVPActivity {

    private Button button;
    private TextView textView;
    private Button btn;
    private TextView tv;


    @Override
    protected void init() {
        setContentView(R.layout.activity_multiple_interface);

        button = findViewById(R.id.button);
        textView = findViewById(R.id.textView);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });


        btn = findViewById(R.id.btn);
        tv = findViewById(R.id.tv);

        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });
    }

    @Override
    protected IPresenter createPresenter() {
        return null;
    }

}
複製代碼

此時咱們能夠想下,當一個頁面中有多個網絡請求時,Activity所繼承的BaseMVPActivity的泛型中要寫多個參數,那有沒有上面代碼的框架不變的狀況下實現這個需求呢?答案必須有的.咱們能夠把多個網絡請求的功能當作一個網絡請求來看待,封裝成一個MultiplePresenter,其繼承至BasePresenter實現生命週期的適配.此MultiplePresenter類的做用就是容納多個Presenter,鏈接同一個View.代碼以下.

public class MultiplePresenter<T extends IView> extends BasePresenter<T> {
    private T mView;

    private List<IPresenter> presenters = new ArrayList<>();

    @SafeVarargs
    public final <K extends IPresenter<T>> void addPresenter(K... addPresenter) {
        for (K ap : addPresenter) {
            ap.attachView(mView);
            presenters.add(ap);
        }
    }

    public MultiplePresenter(T mView) {
        this.mView = mView;
    }

    @Override
    public void detachView() {
        for (IPresenter presenter : presenters) {
            presenter.detachView();
        }
    }

}
複製代碼

因MultiplePresenter類中須要有多個網絡請求,如今舉例說明時,暫時用兩個網絡請求接口.MultipleInterfaceActivity類中代碼改造以下.

public class MultipleInterfaceActivity extends BaseMVPActivity<MultiplePresenter>
        implements SingleInterfaceContract.View, MultipleInterfaceContract.View {

    private Button button;
    private TextView textView;
    private Button btn;
    private TextView tv;
    private SingleInterfacePresenter singleInterfacePresenter;
    private MultipleInterfacePresenter multipleInterfacePresenter;


    @Override
    protected void init() {
        setContentView(R.layout.activity_multiple_interface);

        button = findViewById(R.id.button);
        textView = findViewById(R.id.textView);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                singleInterfacePresenter.getData(0);
            }
        });


        btn = findViewById(R.id.btn);
        tv = findViewById(R.id.tv);

        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                multipleInterfacePresenter.getBanner();
            }
        });
    }

    @Override
    protected MultiplePresenter createPresenter() {
        MultiplePresenter multiplePresenter = new MultiplePresenter(this);

        singleInterfacePresenter = new SingleInterfacePresenter();
        multipleInterfacePresenter = new MultipleInterfacePresenter();

        multiplePresenter.addPresenter(singleInterfacePresenter);
        multiplePresenter.addPresenter(multipleInterfacePresenter);
        return multiplePresenter;
    }

    @Override
    public void showArticleSuccess(ArticleListBean bean) {
        textView.setText(bean.data.datas.get(0).title);
    }

    @Override
    public void showArticleFail(String errorMsg) {
        Toast.makeText(this, errorMsg, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void showMultipleSuccess(BannerBean bean) {
        tv.setText(bean.data.get(0).title);
    }

    @Override
    public void showMultipleFail(String errorMsg) {
        Toast.makeText(this, errorMsg, Toast.LENGTH_SHORT).show();
    }
}

複製代碼

寫到這裏,MVP框架基本算是完成.若是想再次優化,其實仍是有可優化的地方,好比當View銷燬時,如今只是讓P層中的View對象置爲null,並無繼續對M層通知.若是View銷燬時,M層還在請求網絡中呢,能夠爲此再加入一個取消網絡請求的通用功能.這裏只是舉一個例子,每一個人對MVP的理解不同,而MVP架構也並非一成不變,適合本身項目的纔是最好的.

6. 完整項目地址

完整項目已提交到github(github.com/serge66/MVP…),若須要敬請查看.

五. 參考資料

一步步帶你精通MVP

從0到1搭建MVP框架

Presenter層如何高度的複用

六. 後續

<<MVVM架構從入門到精通-真槍實彈>> 敬請期待~~~

原創文章,來自於Vitamio(http://blog.csdn.net/vitamio),轉載請註明出處。

相關文章
相關標籤/搜索