一. 前言
你是否遇到過Activity/Fragment中成百上千行代碼,完全無法維護,看著頭疼?
你是否遇到過因后臺接口還未寫而你不能先寫代碼邏輯的情況?
你是否遇到過用MVC架構寫的項目進行單元測試時的深深無奈?
如果你現(xiàn)在還是用MVC架構模式在寫項目,請先轉到MVP模式!
二. MVC架構
MVC架構模式最初生根于服務器端的Web開發(fā),后來漸漸能夠勝任客戶端Web開發(fā)臀规,再后來因Android項目由XML和Activity/Fragment組成,慢慢的Android開發(fā)者開始使用類似MVC的架構模式開發(fā)應用.
M層:模型層(model),主要是實體類,數(shù)據(jù)庫,網(wǎng)絡等存在的層面,model將新的數(shù)據(jù)發(fā)送到view層,用戶得到數(shù)據(jù)響應.
V層:視圖層(view),一般指XML為代表的視圖界面.顯示來源于model層的數(shù)據(jù).用戶的點擊操作等事件從view層傳遞到controller層.
C層:控制層(controller),一般以Activity/Fragment為代表.C層主要是連接V層和M層的,C層收到V層發(fā)送過來的事件請求,從M層獲取數(shù)據(jù),展示給V層.
從上圖可以看出M層和V層有連接關系,而Activity有時候既充當了控制層又充當了視圖層,導致項目維護比較麻煩.
1. MVC架構優(yōu)缺點
A. 缺點
M層和V層有連接關系,沒有解耦,導致維護困難.
Activity/Fragment中的代碼過多,難以維護.
Activity中有很多關于視圖UI的顯示代碼,因此View視圖和Activity控制器并不是完全分離的,當Activity類業(yè)務過多的時候岛请,會變得難以管理和維護.尤其是當UI的狀態(tài)數(shù)據(jù)贴膘,跟持久化的數(shù)據(jù)混雜在一起灵巧,變得極為混亂.
B. 優(yōu)點
控制層和View層都在Activity中進行操作拼弃,數(shù)據(jù)操作方便.
模塊職責劃分明確.主要劃分層M,V,C三個模塊.
三. MVP架構
MVP,即是Model,View,Presenter架構模式.看起來類似MVC,其實不然.從上圖能看到Model層和View層沒有相連接,完全解耦.
用戶觸碰界面觸發(fā)事件,View層把事件通知Presenter層,Presenter層通知Model層處理這個事件,Model層處理后把結果發(fā)送到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層,起到一個通知傳遞數(shù)據(jù)的作用.
1. MVP架構優(yōu)缺點
A. 缺點
MVP中接口過多.
每一個功能,相比于MVC要多寫好幾個文件.
如果某一個界面中需要請求多個服務器接口,這個界面文件中會實現(xiàn)很多的回調接口,導致代碼繁雜.
如果更改了數(shù)據(jù)源和請求中參數(shù),會導致更多的代碼修改.
額外的代碼復雜度及學習成本.
B. 優(yōu)點
模塊職責劃分明顯,層次清晰,接口功能清晰.
Model層和View層分離,解耦.修改View而不影響Model.
功能復用度高,方便.一個Presenter可以復用于多個View,而不用更改Presenter的邏輯.
有利于測試驅動開發(fā),以前的Android開發(fā)是難以進行單元測試.
如果后臺接口還未寫好,但已知返回數(shù)據(jù)類型的情況下,完全可以寫出此接口完整的功能.
四. MVP架構實戰(zhàn)(真槍實彈)
1. MVP三層代碼簡單書寫
接下來筆者從簡到繁,一點一點的堆砌MVP的整個架構.先看一下XML布局,布局中一個Button按鈕和一個TextView控件,用戶點擊按鈕后,Presenter層通知Model層請求處理網(wǎng)絡數(shù)據(jù),處理后Model層把結果數(shù)據(jù)發(fā)送給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="請點擊上方按鈕獲取數(shù)據(jù)" />
</LinearLayout>
接下來是Activity代碼,里面就是獲取Button和TextView控件,然后對Button做監(jiān)聽,先簡單的這樣寫,一會慢慢的增加代碼.
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)絡請求用的是wanandroid網(wǎng)站的開放api,其中的文章首頁列表接口.SingleInterfaceModel文件里面有一個方法getData,第一個參數(shù)curPage意思是獲取第幾頁的數(shù)據(jù),第二個參數(shù)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("出現(xiàn)錯誤");
}
@Override
public void onNext(ArticleListBean bean) {
if (null == bean) {
callback.onFail("出現(xiàn)錯誤");
} else if (bean.errorCode != 0) {
callback.onFail(bean.errorMsg);
} else {
callback.onSuccess(bean);
}
}
});
}
}
Callback文件內容如下.里面一個成功一個失敗的回調接口,參數(shù)全是泛型,為啥使用泛型筆者就不用說了吧.
public interface Callback<K, V> {
void onSuccess(K data);
void onFail(V data);
}
再接下來是Presenter層的代碼.SingleInterfacePresenter類構造函數(shù)中直接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層請求數(shù)據(jù)成功,則此處應執(zhí)行通知View層的代碼
}
@Override
public void onFail(String errorMsg) {
//如果Model層請求數(shù)據(jù)失敗,則此處應執(zhí)行通知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層按鈕的監(jiān)聽事件執(zhí)行調用了Presenter層對象的getData方法,此時,Presenter層對象的getData方法調用了Model層對象的getData方法,Model層對象的getData方法中執(zhí)行了網(wǎng)絡請求和邏輯處理,把成功或失敗的結果通過Callback接口回調給了Presenter層,然后Presenter層再通知View層改變界面.但此時SingleInterfacePresenter類中收到Model層的結果后無法通知View層,因為SingleInterfacePresenter未持有View層的對象.如下代碼的注釋中有說明.(如果此時點擊按鈕,下方代碼LP.w()處會打印出網(wǎ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層請求數(shù)據(jù)成功,則此處應執(zhí)行通知View層的代碼
//LP.w()是一個簡單的log打印
LP.w(loginResultBean.toString());
}
@Override
public void onFail(String errorMsg) {
//如果Model層請求數(shù)據(jù)失敗,則此處應執(zhí)行通知View層的代碼
}
});
}
}
代碼寫到這里,筆者先把這些代碼提交到github(https://github.com/serge66/MVPDemo),github上會有一次提交記錄,如果想看此時的代碼,可以根據(jù)提交記錄"第一次修改"克隆此時的代碼.
2. P層V層溝通橋梁
現(xiàn)在P層未持有V層對象,不能通知V層改變界面,那么就繼續(xù)演變MVP架構.
在MVP架構中,我們要為每個Activity/Fragment寫一個接口,這個接口需要讓Presenter層持有,P層通過這個接口去通知V層更改界面.接口中包含了成功和失敗的回調,這個接口Activity/Fragment要去實現(xiàn),最終P層才能通知V層.
public interface SingleInterfaceIView {
void showArticleSuccess(ArticleListBean bean);
void showArticleFail(String errorMsg);
}
一個完整的項目以后肯定會有許多功能界面,那么我們應該抽出一個IView公共接口,讓所有的Activity/Fragment都間接實現(xiàn)它.IVew公共接口是用于給View層的接口繼承的,注意,不是View本身繼承.因為它定義的是接口的規(guī)范, 而其他接口才是定義的類的規(guī)范(這句話請仔細理解).
/**
* @Description: 公共接口 是用于給View的接口繼承的夏伊,注意摇展,不是View本身繼承吻氧。
* 因為它定義的是接口的規(guī)范, 而其他接口才是定義的類的規(guī)范
* @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 {
}
現(xiàn)在項目中Model層是一個SingleInterfaceModel類,這個類對象被P層持有,對于面向對象設計來講,利用接口達到解耦目的已經(jīng)人盡皆知,那我們就要對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("出現(xiàn)錯誤");
}
@Override
public void onNext(ArticleListBean bean) {
if (null == bean) {
callback.onFail("出現(xiàn)錯誤");
} 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層請求數(shù)據(jù)成功,則此處應執(zhí)行通知View層的代碼
//LP.w()是一個簡單的log打印
LP.w(loginResultBean.toString());
}
@Override
public void onFail(String errorMsg) {
//如果Model層請求數(shù)據(jù)失敗,則此處應執(zhí)行通知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是否已經(jīng)銷毀
*
* @return
*/
boolean isViewAttached();
}
這個IPresenter接口需要所有的P層實現(xiàn)類繼承,對于生命周期這部分功能都是通用的,那么就可以抽出來一個抽象基類BasePresenter,去實現(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;
}
}
此時,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層請求數(shù)據(jù)成功,則此處應執(zhí)行通知View層的代碼
//LP.w()是一個簡單的log打印
LP.w(loginResultBean.toString());
if (isViewAttached()) {
mView.showArticleSuccess(loginResultBean);
}
}
@Override
public void onFail(String errorMsg) {
//如果Model層請求數(shù)據(jù)失敗,則此處應執(zhí)行通知View層的代碼
LP.w(errorMsg);
if (isViewAttached()) {
mView.showArticleFail(errorMsg);
}
}
});
}
}
此時,P層和V層的連接橋梁已經(jīng)搭建,但還未搭建完成,我們需要寫個BaseMVPActvity讓所有的Activity繼承,統(tǒng)一處理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();
}
/**
* 創(chuàng)建一個Presenter
*
* @return
*/
protected abstract T createPresenter();
protected abstract void init();
}
接下來讓SingleInterfaceActivity實現(xiàn)這個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(https://github.com/serge66/MVPDemo),github上會有一次提交記錄,如果想看此時的代碼,可以根據(jù)提交記錄"第二次修改"克隆此時的代碼.
4. 優(yōu)化MVP架構
上面是MVP的目錄,從目錄中我們可以看到一個功能點(網(wǎng)絡請求)MVP三層各有兩個文件需要寫,相對于MVC來說寫起來確實麻煩,這也是一些人不愿意寫MVP,寧愿用MVC的原因.
這里我們可以對此優(yōu)化一下.MVP架構中有個Contract的概念,Contract有統(tǒng)一管理接口的作用,目的是為了統(tǒng)一管理一個頁面的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);
}
}
此時,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層請求數(shù)據(jù)成功,則此處應執(zhí)行通知View層的代碼
//LP.w()是一個簡單的log打印
LP.w(loginResultBean.toString());
if (isViewAttached()) {
mView.showArticleSuccess(loginResultBean);
}
}
@Override
public void onFail(String errorMsg) {
//如果Model層請求數(shù)據(jù)失敗,則此處應執(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上會有一次提交記錄,如果想看此時的代碼,可以根據(jù)提交記錄"第三次修改"克隆此時的代碼.
5. 單頁面多網(wǎng)絡請求
上面的MVP封裝只適用于單頁面一個網(wǎng)絡請求的情況,當一個界面有兩個網(wǎng)絡請求時,此封裝已不適合.為此,我們再次新建一個MultipleInterfaceActivity來進行說明.XML中布局是兩個按鈕兩個Textview,點擊則可以進行網(wǎng)絡請求.
<?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="請點擊上方按鈕獲取數(shù)據(jù)" />
<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="請點擊上方按鈕獲取數(shù)據(jù)" />
</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;
}
}
此時我們可以想下,當一個頁面中有多個網(wǎng)絡請求時,Activity所繼承的BaseMVPActivity的泛型中要寫多個參數(shù),那有沒有上面代碼的框架不變的情況下實現(xiàn)這個需求呢?答案必須有的.我們可以把多個網(wǎng)絡請求的功能當做一個網(wǎng)絡請求來看待,封裝成一個MultiplePresenter,其繼承至BasePresenter實現(xiàn)生命周期的適配.此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類中需要有多個網(wǎng)絡請求,現(xiàn)在舉例說明時,暫時用兩個網(wǎ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)化,其實還是有可優(yōu)化的地方,比如當View銷毀時,現(xiàn)在只是讓P層中的View對象置為null,并沒有繼續(xù)對M層通知.如果View銷毀時,M層還在請求網(wǎng)絡中呢,可以為此再加入一個取消網(wǎng)絡請求的通用功能.這里只是舉一個例子,每個人對MVP的理解不一樣,而MVP架構也并不是一成不變,適合自己項目的才是最好的.
6. 完整項目地址
完整項目已提交到github(https://github.com/serge66/MVPDemo),若需要敬請查看.
五. 參考資料
一步步帶你精通MVP
從0到1搭建MVP框架
Presenter層如何高度的復用
六. 后續(xù)
<<MVVM架構從入門到精通-真槍實彈>> 敬請期待~~~
原創(chuàng)文章咏连,來自于Vitamio(http://blog.csdn.net/vitamio)盯孙,轉載請注明出處。