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

一. 前言
你是否遇到過Activity/Fragment中成百上千行程式碼,完全無法維護,看著頭疼?
你是否遇到過因後臺介面還未寫而你不能先寫程式碼邏輯的情況?
你是否遇到過用MVC架構寫的專案進行單元測試時的深深無奈?
如果你現在還是用MVC架構模式在寫專案,請先轉到MVP模式!
二. MVC架構
MVC架構模式最初生根於伺服器端的Web開發,後來漸漸能夠勝任客戶端Web開發,再後來因Android專案由XML和Activity/Fragment組成,慢慢的Android開發者開始使用類似MVC的架構模式開發應用.

M層:模型層(model),主要是實體類,資料庫,網路等存在的層面,model將新的資料傳送到view層,使用者得到資料響應.
V層:檢視層(view),一般指XML為代表的檢視介面.顯示來源於model層的資料.使用者的點選操作等事件從view層傳遞到controller層.
C層:控制層(controller),一般以Activity/Fragment為代表.C層主要是連線V層和M層的,C層收到V層傳送過來的事件請求,從M層獲取資料,展示給V層.
從上圖可以看出M層和V層有連線關係,而Activity有時候既充當了控制層又充當了檢視層,導致專案維護比較麻煩.
1. MVC架構優缺點
A. 缺點
-
M層和V層有連線關係,沒有解耦,導致維護困難.
-
Activity/Fragment中的程式碼過多,難以維護.
Activity中有很多關於檢視UI的顯示程式碼,因此View檢視和Activity控制器並不是完全分離的,當Activity類業務過多的時候,會變得難以管理和維護.尤其是當UI的狀態資料,跟持久化的資料混雜在一起,變得極為混亂.
B. 優點
-
控制層和View層都在Activity中進行操作,資料操作方便.
-
模組職責劃分明確.主要劃分層M,V,C三個模組.
三. 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. 缺點
-
MVP中介面過多.
-
每一個功能,相比於MVC要多寫好幾個檔案.
-
如果某一個介面中需要請求多個伺服器介面,這個介面檔案中會實現很多的回撥介面,導致程式碼繁雜.
-
如果更改了資料來源和請求中引數,會導致更多的程式碼修改.
-
額外的程式碼複雜度及學習成本.
B. 優點
-
模組職責劃分明顯,層次清晰,介面功能清晰.
-
Model層和View層分離,解耦.修改View而不影響Model.
-
功能複用度高,方便.一個Presenter可以複用於多個View,而不用更改Presenter的邏輯.
-
有利於測試驅動開發,以前的Android開發是難以進行單元測試.
-
如果後臺介面還未寫好,但已知返回資料型別的情況下,完全可以寫出此介面完整的功能.
四. MVP架構實戰(真槍實彈)
1. MVP三層程式碼簡單書寫
接下來筆者從簡到繁,一點一點的堆砌MVP的整個架構.先看一下XML佈局,佈局中一個Button按鈕和一個TextView控制元件,使用者點選按鈕後,Presenter層通知Model層請求處理網路資料,處理後Model層把結果資料傳送給Presenter層,Presenter層再通知View層,然後View層改變TextView顯示的內容.

<?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( ofollow,noindex">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: [email protected] * @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三層各有兩個檔案需要寫,相對於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,點選則可以進行網路請求.

<?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架構也並不是一成不變,適合自己專案的才是最好的.