Android MVP架構(gòu)從入門到精通-真槍實(shí)彈

文章目錄

一. 前言

你是否遇到過(guò)Activity/Fragment中成百上千行代碼,完全無(wú)法維護(hù),看著頭疼?

你是否遇到過(guò)因后臺(tái)接口還未寫而你不能先寫代碼邏輯的情況?

你是否遇到過(guò)用MVC架構(gòu)寫的項(xiàng)目進(jìn)行單元測(cè)試時(shí)的深深無(wú)奈?

如果你現(xiàn)在還是用MVC架構(gòu)模式在寫項(xiàng)目,請(qǐng)先轉(zhuǎn)到MVP模式!

二. MVC架構(gòu)

MVC架構(gòu)模式最初生根于服務(wù)器端的Web開發(fā),后來(lái)漸漸能夠勝任客戶端Web開發(fā),再后來(lái)因Android項(xiàng)目由XML和Activity/Fragment組成,慢慢的Android開發(fā)者開始使用類似MVC的架構(gòu)模式開發(fā)應(yīng)用.

mvc架構(gòu)模式

M層:模型層(model),主要是實(shí)體類,數(shù)據(jù)庫(kù),網(wǎng)絡(luò)等存在的層面,model將新的數(shù)據(jù)發(fā)送到view層,用戶得到數(shù)據(jù)響應(yīng).

V層:視圖層(view),一般指XML為代表的視圖界面.顯示來(lái)源于model層的數(shù)據(jù).用戶的點(diǎn)擊操作等事件從view層傳遞到controller層.

C層:控制層(controller),一般以Activity/Fragment為代表.C層主要是連接V層和M層的,C層收到V層發(fā)送過(guò)來(lái)的事件請(qǐng)求,從M層獲取數(shù)據(jù),展示給V層.

從上圖可以看出M層和V層有連接關(guān)系,而Activity有時(shí)候既充當(dāng)了控制層又充當(dāng)了視圖層,導(dǎo)致項(xiàng)目維護(hù)比較麻煩.

1. MVC架構(gòu)優(yōu)缺點(diǎn)
A. 缺點(diǎn)
  1. M層和V層有連接關(guān)系,沒(méi)有解耦,導(dǎo)致維護(hù)困難.

  2. Activity/Fragment中的代碼過(guò)多,難以維護(hù).

Activity中有很多關(guān)于視圖UI的顯示代碼,因此View視圖和Activity控制器并不是完全分離的,當(dāng)Activity類業(yè)務(wù)過(guò)多的時(shí)候,會(huì)變得難以管理和維護(hù).尤其是當(dāng)UI的狀態(tài)數(shù)據(jù),跟持久化的數(shù)據(jù)混雜在一起,變得極為混亂.

B. 優(yōu)點(diǎn)
  1. 控制層和View層都在Activity中進(jìn)行操作,數(shù)據(jù)操作方便.

  2. 模塊職責(zé)劃分明確.主要?jiǎng)澐謱覯,V,C三個(gè)模塊.

三. MVP架構(gòu)

MVP

MVP,即是Model,View,Presenter架構(gòu)模式.看起來(lái)類似MVC,其實(shí)不然.從上圖能看到Model層和View層沒(méi)有相連接,完全解耦.

用戶觸碰界面觸發(fā)事件,View層把事件通知Presenter層,Presenter層通知Model層處理這個(gè)事件,Model層處理后把結(jié)果發(fā)送到Presenter層,Presenter層再通知View層,最后View層做出改變.這是一整套流程.

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

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

P層:通知層(Presenter),P層的主要作用就是連接V層和M層,起到一個(gè)通知傳遞數(shù)據(jù)的作用.

1. MVP架構(gòu)優(yōu)缺點(diǎn)
A. 缺點(diǎn)
  1. MVP中接口過(guò)多.

  2. 每一個(gè)功能,相比于MVC要多寫好幾個(gè)文件.

  3. 如果某一個(gè)界面中需要請(qǐng)求多個(gè)服務(wù)器接口,這個(gè)界面文件中會(huì)實(shí)現(xiàn)很多的回調(diào)接口,導(dǎo)致代碼繁雜.

  4. 如果更改了數(shù)據(jù)源和請(qǐng)求中參數(shù),會(huì)導(dǎo)致更多的代碼修改.

  5. 額外的代碼復(fù)雜度及學(xué)習(xí)成本.

B. 優(yōu)點(diǎn)
  1. 模塊職責(zé)劃分明顯,層次清晰,接口功能清晰.

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

  3. 功能復(fù)用度高,方便.一個(gè)Presenter可以復(fù)用于多個(gè)View,而不用更改Presenter的邏輯.

  4. 有利于測(cè)試驅(qū)動(dòng)開發(fā),以前的Android開發(fā)是難以進(jìn)行單元測(cè)試.

  5. 如果后臺(tái)接口還未寫好,但已知返回?cái)?shù)據(jù)類型的情況下,完全可以寫出此接口完整的功能.

四. MVP架構(gòu)實(shí)戰(zhàn)(真槍實(shí)彈)

1. MVP三層代碼簡(jiǎn)單書寫

接下來(lái)筆者從簡(jiǎn)到繁,一點(diǎn)一點(diǎn)的堆砌MVP的整個(gè)架構(gòu).先看一下XML布局,布局中一個(gè)Button按鈕和一個(gè)TextView控件,用戶點(diǎn)擊按鈕后,Presenter層通知Model層請(qǐng)求處理網(wǎng)絡(luò)數(shù)據(jù),處理后Model層把結(jié)果數(shù)據(jù)發(fā)送給Presenter層,Presenter層再通知View層,然后View層改變TextView顯示的內(nèi)容.

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="點(diǎn)擊" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="100px"
        android:text="請(qǐng)點(diǎn)擊上方按鈕獲取數(shù)據(jù)" />
</LinearLayout>

接下來(lái)是Activity代碼,里面就是獲取Button和TextView控件,然后對(duì)Button做監(jiān)聽,先簡(jiǎn)單的這樣寫,一會(huì)慢慢的增加代碼.

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層代碼.本次網(wǎng)絡(luò)請(qǐng)求用的是wanandroid網(wǎng)站的開放api,其中的文章首頁(yè)列表接口.SingleInterfaceModel文件里面有一個(gè)方法getData,第一個(gè)參數(shù)curPage意思是獲取第幾頁(yè)的數(shù)據(jù),第二個(gè)參數(shù)callback是Model層通知Presenter層的回調(diào).

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("出現(xiàn)錯(cuò)誤");
                    }

                    @Override
                    public void onNext(ArticleListBean bean) {
                        if (null == bean) {
                            callback.onFail("出現(xiàn)錯(cuò)誤");
                        } else if (bean.errorCode != 0) {
                            callback.onFail(bean.errorMsg);
                        } else {
                            callback.onSuccess(bean);
                        }
                    }
                });
    }
}

Callback文件內(nèi)容如下.里面一個(gè)成功一個(gè)失敗的回調(diào)接口,參數(shù)全是泛型,為啥使用泛型筆者就不用說(shuō)了吧.

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

    void onFail(V data);
}

再接下來(lái)是Presenter層的代碼.SingleInterfacePresenter類構(gòu)造函數(shù)中直接new了一個(gè)Model層對(duì)象,用于Presenter層對(duì)Model層的調(diào)用.然后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層請(qǐng)求數(shù)據(jù)成功,則此處應(yīng)執(zhí)行通知View層的代碼
                
            }

            @Override
            public void onFail(String errorMsg) {
                //如果Model層請(qǐng)求數(shù)據(jù)失敗,則此處應(yīng)執(zhí)行通知View層的代碼
                
            }
        });
    }
}

至此,MVP三層簡(jiǎn)單的部分代碼算是完成.那么怎樣進(jìn)行整個(gè)流程的相互調(diào)用呢.我們把剛開始的SingleInterfaceActivity代碼改一下,讓SingleInterfaceActivity持有Presenter層的對(duì)象,這樣View層就可以調(diào)用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);
            }
        });

    }
}

從以上所有代碼可以看出,當(dāng)用戶點(diǎn)擊按鈕后,View層按鈕的監(jiān)聽事件執(zhí)行調(diào)用了Presenter層對(duì)象的getData方法,此時(shí),Presenter層對(duì)象的getData方法調(diào)用了Model層對(duì)象的getData方法,Model層對(duì)象的getData方法中執(zhí)行了網(wǎng)絡(luò)請(qǐng)求和邏輯處理,把成功或失敗的結(jié)果通過(guò)Callback接口回調(diào)給了Presenter層,然后Presenter層再通知View層改變界面.但此時(shí)SingleInterfacePresenter類中收到Model層的結(jié)果后無(wú)法通知View層,因?yàn)镾ingleInterfacePresenter未持有View層的對(duì)象.如下代碼的注釋中有說(shuō)明.(如果此時(shí)點(diǎn)擊按鈕,下方代碼LP.w()處會(huì)打印出網(wǎng)絡(luò)請(qǐng)求成功的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層請(qǐng)求數(shù)據(jù)成功,則此處應(yīng)執(zhí)行通知View層的代碼
                //LP.w()是一個(gè)簡(jiǎn)單的log打印
                LP.w(loginResultBean.toString());
            }

            @Override
            public void onFail(String errorMsg) {
                //如果Model層請(qǐng)求數(shù)據(jù)失敗,則此處應(yīng)執(zhí)行通知View層的代碼

            }
        });
    }
}

代碼寫到這里,筆者先把這些代碼提交到github(https://github.com/serge66/MVPDemo),github上會(huì)有一次提交記錄,如果想看此時(shí)的代碼,可以根據(jù)提交記錄"第一次修改"克隆此時(shí)的代碼.

2. P層V層溝通橋梁

現(xiàn)在P層未持有V層對(duì)象,不能通知V層改變界面,那么就繼續(xù)演變MVP架構(gòu).
在MVP架構(gòu)中,我們要為每個(gè)Activity/Fragment寫一個(gè)接口,這個(gè)接口需要讓Presenter層持有,P層通過(guò)這個(gè)接口去通知V層更改界面.接口中包含了成功和失敗的回調(diào),這個(gè)接口Activity/Fragment要去實(shí)現(xiàn),最終P層才能通知V層.

public interface SingleInterfaceIView {
    void showArticleSuccess(ArticleListBean bean);

    void showArticleFail(String errorMsg);
}

一個(gè)完整的項(xiàng)目以后肯定會(huì)有許多功能界面,那么我們應(yīng)該抽出一個(gè)IView公共接口,讓所有的Activity/Fragment都間接實(shí)現(xiàn)它.IVew公共接口是用于給View層的接口繼承的,注意,不是View本身繼承.因?yàn)樗x的是接口的規(guī)范, 而其他接口才是定義的類的規(guī)范(這句話請(qǐng)仔細(xì)理解).

/**
 * @Description: 公共接口 是用于給View的接口繼承的,注意,不是View本身繼承。
 *                  因?yàn)樗x的是接口的規(guī)范, 而其他接口才是定義的類的規(guī)范
 * @Author: lishengjiejob@163.com
 * @Time: 2018/11/22 17:26
 */
public interface IView {
}

這個(gè)接口中可以寫一些所有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 {
}

現(xiàn)在項(xiàng)目中Model層是一個(gè)SingleInterfaceModel類,這個(gè)類對(duì)象被P層持有,對(duì)于面向?qū)ο笤O(shè)計(jì)來(lái)講,利用接口達(dá)到解耦目的已經(jīng)人盡皆知,那我們就要對(duì)SingleInterfaceModel類再寫一個(gè)可繼承的接口.代碼如下.

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("出現(xiàn)錯(cuò)誤");
                    }

                    @Override
                    public void onNext(ArticleListBean bean) {
                        if (null == bean) {
                            callback.onFail("出現(xiàn)錯(cuò)誤");
                        } else if (bean.errorCode != 0) {
                            callback.onFail(bean.errorMsg);
                        } else {
                            callback.onSuccess(bean);
                        }
                    }
                });
    }
}

同理,View層持有P層對(duì)象,我們也需要對(duì)P層進(jìn)行改造.但是下面的代碼卻沒(méi)有像ISingleInterfaceModel接口繼承IModel一樣繼承IPresenter,這點(diǎn)需要注意,筆者把IPresenter的繼承放在了其他處,后面會(huì)講解.

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層請(qǐng)求數(shù)據(jù)成功,則此處應(yīng)執(zhí)行通知View層的代碼
                //LP.w()是一個(gè)簡(jiǎn)單的log打印
                LP.w(loginResultBean.toString());
            }

            @Override
            public void onFail(String errorMsg) {
                //如果Model層請(qǐng)求數(shù)據(jù)失敗,則此處應(yīng)執(zhí)行通知View層的代碼
                LP.w(errorMsg);
            }
        });
    }
}
3. 生命周期適配

至此,MVP三層每層的接口都寫好了.但是P層連接V層的橋梁還沒(méi)有搭建好,這個(gè)慢慢來(lái),一個(gè)好的高樓大廈都是一步一步建造的.上面IPresenter接口我們沒(méi)有讓其他類繼承,接下來(lái)就講下這個(gè).P層和V層相連接,V層的生命周期也要適配到P層,P層的每個(gè)功能都要適配生命周期,這里可以把生命周期的適配放在IPresenter接口中.P層持有V層對(duì)象,這里把它放到泛型中.代碼如下.

public interface IPresenter<T extends IView> {

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

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

    /**
     * 判斷View是否已經(jīng)銷毀
     *
     * @return
     */
    boolean isViewAttached();
    
}

這個(gè)IPresenter接口需要所有的P層實(shí)現(xiàn)類繼承,對(duì)于生命周期這部分功能都是通用的,那么就可以抽出來(lái)一個(gè)抽象基類BasePresenter,去實(shí)現(xiàn)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;
    }
}

此時(shí),SingleInterfacePresenter類的代碼修改如下.泛型中的SingleInterfaceIView可以理解成對(duì)應(yīng)的Activity,P層此時(shí)完成了對(duì)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層請(qǐng)求數(shù)據(jù)成功,則此處應(yīng)執(zhí)行通知View層的代碼
                //LP.w()是一個(gè)簡(jiǎn)單的log打印
                LP.w(loginResultBean.toString());
                if (isViewAttached()) {
                    mView.showArticleSuccess(loginResultBean);
                }
            }

            @Override
            public void onFail(String errorMsg) {
                //如果Model層請(qǐng)求數(shù)據(jù)失敗,則此處應(yīng)執(zhí)行通知View層的代碼
                LP.w(errorMsg);
                if (isViewAttached()) {
                    mView.showArticleFail(errorMsg);
                }
            }
        });
    }
}

此時(shí),P層和V層的連接橋梁已經(jīng)搭建,但還未搭建完成,我們需要寫個(gè)BaseMVPActvity讓所有的Activity繼承,統(tǒng)一處理Activity相同邏輯.在BaseMVPActvity中使用IPresenter的泛型,因?yàn)槊總€(gè)Activity中需要持有P層對(duì)象,這里把P層對(duì)象抽出來(lái)也放在BaseMVPActvity中.同時(shí)BaseMVPActvity中也需要繼承IView,用于P層對(duì)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();
    }

    /**
     * 創(chuàng)建一個(gè)Presenter
     *
     * @return
     */
    protected abstract T createPresenter();

    protected abstract void init();

}

接下來(lái)讓SingleInterfaceActivity實(shí)現(xiàn)這個(gè)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架構(gòu)的整個(gè)簡(jiǎn)易流程完成.

代碼寫到這里,筆者先把這些代碼提交到github(https://github.com/serge66/MVPDemo),github上會(huì)有一次提交記錄,如果想看此時(shí)的代碼,可以根據(jù)提交記錄"第二次修改"克隆此時(shí)的代碼.

4. 優(yōu)化MVP架構(gòu)
MVP目錄

上面是MVP的目錄,從目錄中我們可以看到一個(gè)功能點(diǎn)(網(wǎng)絡(luò)請(qǐng)求)MVP三層各有兩個(gè)文件需要寫,相對(duì)于MVC來(lái)說(shuō)寫起來(lái)確實(shí)麻煩,這也是一些人不愿意寫MVP,寧愿用MVC的原因.

這里我們可以對(duì)此優(yōu)化一下.MVP架構(gòu)中有個(gè)Contract的概念,Contract有統(tǒng)一管理接口的作用,目的是為了統(tǒng)一管理一個(gè)頁(yè)面的View和Presenter接口,用Contract可以減少部分文件的創(chuàng)建,比如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);
    }


}

此時(shí),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層請(qǐng)求數(shù)據(jù)成功,則此處應(yīng)執(zhí)行通知View層的代碼
                //LP.w()是一個(gè)簡(jiǎn)單的log打印
                LP.w(loginResultBean.toString());
                if (isViewAttached()) {
                    mView.showArticleSuccess(loginResultBean);
                }
            }

            @Override
            public void onFail(String errorMsg) {
                //如果Model層請(qǐng)求數(shù)據(jù)失敗,則此處應(yīng)執(zhí)行通知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(https://github.com/serge66/MVPDemo),github上會(huì)有一次提交記錄,如果想看此時(shí)的代碼,可以根據(jù)提交記錄"第三次修改"克隆此時(shí)的代碼.

5. 單頁(yè)面多網(wǎng)絡(luò)請(qǐng)求

上面的MVP封裝只適用于單頁(yè)面一個(gè)網(wǎng)絡(luò)請(qǐng)求的情況,當(dāng)一個(gè)界面有兩個(gè)網(wǎng)絡(luò)請(qǐng)求時(shí),此封裝已不適合.為此,我們?cè)俅涡陆ㄒ粋€(gè)MultipleInterfaceActivity來(lái)進(jìn)行說(shuō)明.XML中布局是兩個(gè)按鈕兩個(gè)Textview,點(diǎn)擊則可以進(jìn)行網(wǎng)絡(luò)請(qǐng)求.

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="點(diǎn)擊" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="50px"
        android:text="請(qǐng)點(diǎn)擊上方按鈕獲取數(shù)據(jù)" />

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

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="50px"
        android:text="請(qǐng)點(diǎn)擊上方按鈕獲取數(shù)據(jù)" />
</LinearLayout>

MultipleInterfaceActivity類代碼暫時(shí)如下.

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;
    }

}

此時(shí)我們可以想下,當(dāng)一個(gè)頁(yè)面中有多個(gè)網(wǎng)絡(luò)請(qǐng)求時(shí),Activity所繼承的BaseMVPActivity的泛型中要寫多個(gè)參數(shù),那有沒(méi)有上面代碼的框架不變的情況下實(shí)現(xiàn)這個(gè)需求呢?答案必須有的.我們可以把多個(gè)網(wǎng)絡(luò)請(qǐng)求的功能當(dāng)做一個(gè)網(wǎng)絡(luò)請(qǐng)求來(lái)看待,封裝成一個(gè)MultiplePresenter,其繼承至BasePresenter實(shí)現(xiàn)生命周期的適配.此MultiplePresenter類的作用就是容納多個(gè)Presenter,連接同一個(gè)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類中需要有多個(gè)網(wǎng)絡(luò)請(qǐng)求,現(xiàn)在舉例說(shuō)明時(shí),暫時(shí)用兩個(gè)網(wǎng)絡(luò)請(qǐng)求接口.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框架基本算是完成.如果想再次優(yōu)化,其實(shí)還是有可優(yōu)化的地方,比如當(dāng)View銷毀時(shí),現(xiàn)在只是讓P層中的View對(duì)象置為null,并沒(méi)有繼續(xù)對(duì)M層通知.如果View銷毀時(shí),M層還在請(qǐng)求網(wǎng)絡(luò)中呢,可以為此再加入一個(gè)取消網(wǎng)絡(luò)請(qǐng)求的通用功能.這里只是舉一個(gè)例子,每個(gè)人對(duì)MVP的理解不一樣,而MVP架構(gòu)也并不是一成不變,適合自己項(xiàng)目的才是最好的.

6. 完整項(xiàng)目地址

完整項(xiàng)目已提交到github(https://github.com/serge66/MVPDemo),若需要敬請(qǐng)查看.

五. 參考資料

一步步帶你精通MVP
從0到1搭建MVP框架
Presenter層如何高度的復(fù)用

六. 后續(xù)

<<MVVM架構(gòu)從入門到精通-真槍實(shí)彈>> 敬請(qǐng)期待~~~

原創(chuàng)文章,來(lái)自于Vitamio(http://blog.csdn.net/vitamio),轉(zhuǎn)載請(qǐng)注明出處。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容