本文由玉剛說寫作平臺(tái)提供寫作贊助,版權(quán)歸玉剛說微信公眾號(hào)所有
原作者:Zackratos
版權(quán)聲明:未經(jīng)玉剛說許可,不得以任何形式轉(zhuǎn)載
什么是 MVP
MVP 全稱:Model-View-Presenter 均驶;MVP 是從經(jīng)典的模式 MVC 演變而來阅虫,它們的基本思想有相通的地方:Controller/Presenter 負(fù)責(zé)邏輯的處理,Model 提供數(shù)據(jù)肤晓,View 負(fù)責(zé)顯示(摘自百度百科)安皱。
為什么要使用 MVP
在討論為什么要使用 MVP 架構(gòu)之前调鬓,我們首先要了解傳統(tǒng)的 MVC 的架構(gòu)的特點(diǎn)及其缺點(diǎn)。
首先看一下 MVC 架構(gòu)的模型圖酌伊,如下
這個(gè)圖很簡單腾窝,當(dāng) View 需要更新時(shí),首先去找 Controller,然后 Controller 找 Model 獲取數(shù)據(jù)虹脯,Model 獲取到數(shù)據(jù)之后直接更新 View辜贵。
在 MVC 里,View 是可以直接訪問 Model 的归形。從而,View 里會(huì)包含 Model 信息鼻由,不可避免的還要包括一些業(yè)務(wù)邏輯暇榴。 在 MVC 模型里,更關(guān)注的 Model 的不變蕉世,而同時(shí)有多個(gè)對(duì) Model 的不同顯示蔼紧,即 View。所以狠轻,在 MVC 模型里奸例,Model 不依賴于 View,但是View 是依賴于 Model 的向楼。不僅如此查吊,因?yàn)橛幸恍I(yè)務(wù)邏輯在 View 里實(shí)現(xiàn)了,導(dǎo)致要更改 View 也是比較困難的湖蜕,至少那些業(yè)務(wù)邏輯是無法重用的逻卖。
這樣說可能會(huì)有點(diǎn)抽象,下面通過一個(gè)簡單的例子來說明昭抒。
假設(shè)現(xiàn)在有這樣一個(gè)需求评也,Activity 中有一個(gè) Button 和一個(gè) TextView,點(diǎn)擊 Button 時(shí)會(huì)請(qǐng)求網(wǎng)絡(luò)獲取一段字符串灭返,然后把字符串顯示在 TextView 中盗迟,按照正常的邏輯,代碼應(yīng)該這么寫
public class MVCActivity extends AppCompatActivity {
private Button button;
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
button = findViewById(R.id.button);
textView = findViewById(R.id.text_view);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new HttpModel(textView).request();
}
});
}
}
public class HttpModel {
private TextView textView;
public HttpModel(TextView textView) {
this.textView = textView;
}
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
textView.setText((String) msg.obj);
}
};
public void request() {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
Message msg = handler.obtainMessage();
msg.obj = "從網(wǎng)絡(luò)獲取到的數(shù)據(jù)";
handler.sendMessage(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
代碼很簡單熙含,當(dāng)點(diǎn)擊 Button 的時(shí)候罚缕,創(chuàng)建一個(gè) HttpModel 對(duì)象,并把 TextView 對(duì)象作為參數(shù)傳入怎静,然后調(diào)用它的 request 方法來請(qǐng)求數(shù)據(jù)怕磨,當(dāng)請(qǐng)求到數(shù)據(jù)之后,切換到主線程中更新 TextView消约,流程完全符合上面的 MVC 架構(gòu)圖肠鲫。
但是這里有個(gè)問題,首先很顯然或粮,HttpModel 就是 Model 層导饲,那么 View 層和 Controller 層呢,我們分析一下 View 層和 Model 層分別干了什么事,在本例中渣锦,View 層主要做的事就是當(dāng)獲取到網(wǎng)絡(luò)數(shù)據(jù)的時(shí)候硝岗,更新 TextView,Controller 層主要做的事就是創(chuàng)建 HttpModel 對(duì)象并調(diào)用它的 request 方法袋毙,我們發(fā)現(xiàn) MVCActivity 同時(shí)充當(dāng)了 View 層和 Controller 層型檀。
這樣會(huì)造成兩個(gè)問題,第一听盖,View 層和 Controller 層沒有分離胀溺,邏輯比較混亂;第二皆看,同樣因?yàn)?View 和 Controller 層的耦合仓坞,導(dǎo)致 Activity 或者 Fragment 很臃腫,代碼量很大腰吟。由于本例比較簡單无埃,所以這兩個(gè)問題都不是很明顯,如果 Activity 中的業(yè)務(wù)量很大毛雇,那么問題就會(huì)體現(xiàn)出來嫉称,開發(fā)和維護(hù)的成本會(huì)很高。
如何使用 MVP
既然 MVC 有這些問題灵疮,那么應(yīng)該如何改進(jìn)呢澎埠,答案就是使用 MVP 的架構(gòu),關(guān)于 MVP 架構(gòu)的定義前面已經(jīng)說了始藕,下面看一下它的模型圖
這個(gè)圖也很簡單蒲稳,當(dāng) View 需要更新數(shù)據(jù)時(shí),首先去找 Presenter伍派,然后 Presenter 去找 Model 請(qǐng)求數(shù)據(jù)江耀,Model 獲取到數(shù)據(jù)之后通知 Presenter,Presenter 再通知 View 更新數(shù)據(jù)诉植,這樣 Model 和 View 就不會(huì)直接交互了祥国,所有的交互都由 Presenter 進(jìn)行,Presenter 充當(dāng)了橋梁的角色晾腔。很顯然舌稀,Presenter 必須同時(shí)持有 View 和 Model 的對(duì)象的引用,才能在它們之間進(jìn)行通信灼擂。
接下來用 MVP 的架構(gòu)來改造上面的例子壁查,代碼如下
interface MVPView {
void updateTv(String text);
}
public class MVPActivity extends AppCompatActivity implements MVPView {
private Button button;
private TextView textView;
private Presenter presenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
button = findViewById(R.id.button);
textView = findViewById(R.id.text_view);
presenter = new Presenter(this);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
presenter.request();
}
});
}
@Override
public void updateTv(String text) {
textView.setText(text);
}
}
interface Callback {
void onResult(String text);
}
public class HttpModel {
private Callback callback;
public HttpModel(Callback callback) {
this.callback = callback;
}
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
callback.onResult((String) msg.obj);
}
};
public void request() {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
Message msg = handler.obtainMessage();
msg.obj = "從網(wǎng)絡(luò)獲取到的數(shù)據(jù)";
handler.sendMessage(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
public class Presenter {
private MVPView view;
private HttpModel model;
public Presenter(MVPView view) {
this.view = view;
model = new HttpModel(new Callback() {
@Override
public void onResult(String text) {
Presenter.this.view.updateTv(text);
}
});
}
public void request() {
model.request();
}
}
簡單解釋一下上面的代碼,首先創(chuàng)建一個(gè) MVPView 的接口剔应,它即時(shí) View 層睡腿,里面有一個(gè)更新 TextView 的方法语御,然后讓 Activity 實(shí)現(xiàn)這個(gè)接口,并復(fù)寫更新 TextView 的方法席怪。Model 層不再傳入 TextView 了应闯,而是傳入一個(gè)回調(diào)接口 Callback,因?yàn)榫W(wǎng)絡(luò)請(qǐng)求獲取數(shù)據(jù)是異步的挂捻,在獲取到數(shù)據(jù)之后需要通過 Callback 來通知 Presenter碉纺。Presenter 也很簡單,首先在它的構(gòu)造方法中刻撒,同時(shí)持有 View 和 Model 的引用骨田,再對(duì)外提供一個(gè) request 方法。
分析一下上面代碼執(zhí)行的流程疫赎,當(dāng)點(diǎn)擊 Button 的時(shí)候,Presenter 調(diào)用 request 方法碎节,在它的內(nèi)部捧搞,通過 Model 調(diào)用 request 方法來請(qǐng)求數(shù)據(jù),請(qǐng)求到數(shù)據(jù)之后狮荔,切換到主線程胎撇,調(diào)用 callback 的 onResult 方法來通知 Presenter,這時(shí)候 Presenter 就會(huì)調(diào)用 View 的 updateTv 方法來更新 TextView殖氏,完成了整個(gè)流程晚树,可以發(fā)現(xiàn),在整個(gè)過程從铆帽,View 和 Model 并沒有直接交互委刘,所有的交互都是在 Presenter 中進(jìn)行的场勤。
注意事項(xiàng)
接口的必要性
可能有的同學(xué)會(huì)問,為什么要寫一個(gè) MVPView 的接口宝鼓,直接把 Activity 本身傳入到 Presenter 不行嗎?這當(dāng)然是可行的巴刻,這里使用接口主要是為了代碼的復(fù)用愚铡,試想一下,如果直接傳入 Activity胡陪,那么這個(gè) Presenter 就只能為這一個(gè) Activity 服務(wù)沥寥。舉個(gè)例子,假設(shè)有個(gè) App 已經(jīng)開發(fā)完成了柠座,可以在手機(jī)上正常使用邑雅,現(xiàn)在要求做平板上的適配,在平板上的界面顯示效果有所變化妈经,TextView 并不是直接在 Activity 中的蒂阱,而是在 Fragment 里面锻全,如果沒有使用 View 的接口的話,那就需要再寫一個(gè)針對(duì) Fragment 的 Presenter录煤,然后把整個(gè)過程再來一遍鳄厌。但是使用 View 的接口就很簡單了,直接讓 Fragment 實(shí)現(xiàn)這個(gè)接口妈踊,然后復(fù)寫接口里面的方法了嚎,Presenter 和 Model 層都不需要做任何改動(dòng)。同理廊营,Model 層也可以采用接口的方式來寫歪泳。
防止內(nèi)存泄漏
其實(shí)上面的代碼存在內(nèi)存泄漏的風(fēng)險(xiǎn)。試想一下露筒,如果在點(diǎn)擊 Button 之后呐伞,Model 獲取到數(shù)據(jù)之前,退出了 Activity慎式,此時(shí)由于 Activity 被 Presenter 引用伶氢,而 Presenter 正在進(jìn)行耗時(shí)操作,會(huì)導(dǎo)致 Activity 的對(duì)象無法被回收瘪吏,造成了內(nèi)存泄漏癣防,解決的方式很簡單,在 Activity 退出的時(shí)候掌眠,把 Presenter 對(duì)中 View 的引用置為空即可蕾盯。
// Presenter.java
public void detachView() {
view = null;
}
// MVPActivity.java
@Override
protected void onDestroy() {
super.onDestroy();
presenter.detachView();
}
另外還有一個(gè)問題,雖然這里 Activity 不會(huì)內(nèi)存泄漏了蓝丙,但是當(dāng) Activity 退出之后级遭,Model 中請(qǐng)求數(shù)據(jù)就沒有意義了,所以還應(yīng)該在 detachView 方法中渺尘,把 Handler 的任務(wù)取消装畅,避免造成資源浪費(fèi),這個(gè)比較簡單沧烈,就不貼代碼了掠兄。
MVP 的封裝
很顯然,MVP 的實(shí)現(xiàn)套路是大致相同的锌雀,如果在一個(gè)應(yīng)用中蚂夕,存在大量的 Activity 和 Fragment,并且都使用 MVP 的架構(gòu)腋逆,那么難免會(huì)有很多重復(fù)工作婿牍,所以封裝就很有必要性了。
在說 MVP 的封裝之前惩歉,需要強(qiáng)調(diào)一點(diǎn)等脂,MVP 更多的是一種思想俏蛮,而不是一種模式,每個(gè)開發(fā)者都可以按照自己的思路來實(shí)現(xiàn)具有個(gè)性化的 MVP上遥,所以不同的人寫出的 MVP 可能會(huì)有一些差別搏屑,筆者在此僅提供一種實(shí)現(xiàn)思路,供讀者參考粉楚。
首先 Model辣恋、View 和 Presenter 都可能會(huì)有一些通用性的操作,所以可以分別定義三個(gè)對(duì)應(yīng)的底層接口模软。
interface BaseModel {
}
interface BaseView {
void showError(String msg);
}
public abstract class BasePresenter<V extends BaseView, M extends BaseModel> {
protected V view;
protected M model;
public BasePresenter() {
model = createModel();
}
void attachView(V view) {
this.view = view;
}
void detachView() {
this.view = null;
}
abstract M createModel();
}
這里的 View 層添加了一個(gè)通用的方法伟骨,顯示錯(cuò)誤信息,寫在接口層燃异,可以在實(shí)現(xiàn)處按照需求來顯示携狭,比如有的地方可能會(huì)是彈出一個(gè) Toast,或者有的地方需要將錯(cuò)誤信息顯示在 TextView 中回俐,Model 層也可以根據(jù)需要添加通用的方法逛腿,重點(diǎn)來看一下 Presenter 層。
這里的 BasePresenter 采用了泛型鲫剿,為什么要這么做呢鳄逾?主要是因?yàn)?Presenter 必須同時(shí)持有 View 和 Model 的引用稻轨,但是在底層接口中無法確定他們的類型灵莲,只能確定他們是 BaseView 和 BaseModel 的子類,所以采用泛型的方式來引用殴俱,就巧妙的解決了這個(gè)問題政冻,在 BasePresenter 的子類中只要定義好 View 和 Model 的類型,就會(huì)自動(dòng)引用他們的對(duì)象了线欲。Presenter 中的通用的方法主要就是 attachView 和 detachView明场,分別用于創(chuàng)建 View 對(duì)象和把 View 的對(duì)象置位空,前面已經(jīng)說過李丰,置空是為了防止內(nèi)存泄漏苦锨,Model 的對(duì)象可以在 Presenter 的構(gòu)造方法中創(chuàng)建。另外趴泌,這里的 Presenter 也可以寫成接口的形式舟舒,讀者可以按照自己的喜好來選擇。
然后看一下在業(yè)務(wù)代碼中該如何使用 MVP 的封裝嗜憔,代碼如下
interface TestContract {
interface Model extends BaseModel {
void getData1(Callback1 callback1);
void getData2(Callback2 callback2);
void getData3(Callback3 callback3);
}
interface View extends BaseView {
void updateUI1();
void updateUI2();
void updateUI3();
}
abstract class Presenter extends BasePresenter<View, Model> {
abstract void request1();
abstract void request2();
void request3() {
model.getData3(new Callback3() {
@Override
public void onResult(String text) {
view.updateUI3();
}
});
}
}
}
首先定義一個(gè) Contract 契約接口秃励,然后把 Model、View吉捶、和 Presenter 的子類分別放入 Contract 的內(nèi)部夺鲜,這里的一個(gè) Contract 就對(duì)應(yīng)一個(gè)頁面(一個(gè) Activity 或者一個(gè) Fragment)皆尔,放在 Contract 內(nèi)部是為了讓同一個(gè)頁面的邏輯方法都放在一起,方便查看和修改币励。Presenter 中的 request3 方法演示了如何通過 Presenter 來進(jìn)行 View 和 Model 的交互慷蠕。
接下來要做的就是實(shí)現(xiàn)這三個(gè)模塊的邏輯方法了,在 Activity 或 Fragment 中實(shí)現(xiàn) TextContract.View 的接口榄审,再分別創(chuàng)建兩個(gè)類用來實(shí)現(xiàn) TextContract.Model 和 TextContract.Presenter砌们,復(fù)寫里面的抽象方法就好了。
擴(kuò)展:用 RxJava 簡化代碼
上面的代碼中搁进,Model 層中的每個(gè)方法都傳入了一個(gè)回調(diào)接口浪感,這是因?yàn)楂@取數(shù)據(jù)往往是異步的,在獲取的數(shù)據(jù)時(shí)需要用回調(diào)接口通知 Presenter 來更新 View饼问。
如果想要避免回調(diào)接口影兽,可以采用 RxJava 的方式來 Model 獲取的數(shù)據(jù)直接返回一個(gè) Observable,接下來用 RxJava 的方式來改造前面的例子
public class HttpModel {
public Observable<String> request() {
return Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
Thread.sleep(2000);
emitter.onNext("從網(wǎng)絡(luò)獲取到的數(shù)據(jù)");
emitter.onComplete();
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
}
public class Presenter {
private MVPView view;
private HttpModel model;
public Presenter(MVPView view) {
this.view = view;
model = new HttpModel();
}
private Disposable disposable;
public void request() {
disposable = model.request()
.subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
view.updateTv(s);
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
}
});
}
public void detachView() {
view = null;
if (disposable != null && !disposable.isDisposed()) {
disposable.dispose();
}
}
}
Model 的 request 方法直接返回一個(gè) Observable莱革,然后在 Presenter 中調(diào)用 subscribe 方法來通知 View 更新峻堰,這樣就避免了使用回調(diào)接口。
開源庫推薦
最后盅视,推薦一個(gè) MVP 架構(gòu)的開源庫捐名,正如筆者所說,MVP 更多的是一種思想闹击,所以 github 上關(guān)于 MVP 的開源庫并不多镶蹋,大多是在完整的 APP 內(nèi)部自己封裝的 MVP。如果想要比較簡單的集成 MVP 的架構(gòu)赏半,筆者推薦這個(gè)庫
https://github.com/sockeqwe/mosby
它的使用方法比較簡單贺归,可以直接參考官方的 demo,接下來簡單的分析一下作者的封裝思想断箫。
首先 View 層和 Presenter 層分別有一個(gè)基礎(chǔ)的接口
public interface MvpView {
}
public interface MvpPresenter<V extends MvpView> {
/**
* Set or attach the view to this presenter
*/
@UiThread
void attachView(V view);
/**
* Will be called if the view has been destroyed. Typically this method will be invoked from
* <code>Activity.detachView()</code> or <code>Fragment.onDestroyView()</code>
*/
@UiThread
void detachView(boolean retainInstance);
}
這里加 @UIThread 注解是為了確保 attachView 和 detachView 都運(yùn)行在主線程中拂酣。
然后業(yè)務(wù)代碼的 Activity 需要繼承 MvpActivity
public abstract class MvpActivity<V extends MvpView, P extends MvpPresenter<V>>
extends AppCompatActivity implements MvpView,
com.hannesdorfmann.mosby3.mvp.delegate.MvpDelegateCallback<V,P> {
protected ActivityMvpDelegate mvpDelegate;
protected P presenter;
protected boolean retainInstance;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getMvpDelegate().onCreate(savedInstanceState);
}
@Override protected void onDestroy() {
super.onDestroy();
getMvpDelegate().onDestroy();
}
@Override protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
getMvpDelegate().onSaveInstanceState(outState);
}
@Override protected void onPause() {
super.onPause();
getMvpDelegate().onPause();
}
@Override protected void onResume() {
super.onResume();
getMvpDelegate().onResume();
}
@Override protected void onStart() {
super.onStart();
getMvpDelegate().onStart();
}
@Override protected void onStop() {
super.onStop();
getMvpDelegate().onStop();
}
@Override protected void onRestart() {
super.onRestart();
getMvpDelegate().onRestart();
}
@Override public void onContentChanged() {
super.onContentChanged();
getMvpDelegate().onContentChanged();
}
@Override protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
getMvpDelegate().onPostCreate(savedInstanceState);
}
/**
* Instantiate a presenter instance
*
* @return The {@link MvpPresenter} for this view
*/
@NonNull public abstract P createPresenter();
/**
* Get the mvp delegate. This is internally used for creating presenter, attaching and detaching
* view from presenter.
*
* <p><b>Please note that only one instance of mvp delegate should be used per Activity
* instance</b>.
* </p>
*
* <p>
* Only override this method if you really know what you are doing.
* </p>
*
* @return {@link ActivityMvpDelegateImpl}
*/
@NonNull protected ActivityMvpDelegate<V, P> getMvpDelegate() {
if (mvpDelegate == null) {
mvpDelegate = new ActivityMvpDelegateImpl(this, this, true);
}
return mvpDelegate;
}
@NonNull @Override public P getPresenter() {
return presenter;
}
@Override public void setPresenter(@NonNull P presenter) {
this.presenter = presenter;
}
@NonNull @Override public V getMvpView() {
return (V) this;
}
}
MvpActivity 中持有一個(gè) ActivityMvpDelegate 對(duì)象,它的實(shí)現(xiàn)類是 ActivityMvpDelegateImpl仲义,并且需要傳入 MvpDelegateCallback 接口婶熬,ActivityMvpDelegateImpl 的代碼如下
public class ActivityMvpDelegateImpl<V extends MvpView, P extends MvpPresenter<V>>
implements ActivityMvpDelegate {
protected static final String KEY_MOSBY_VIEW_ID = "com.hannesdorfmann.mosby3.activity.mvp.id";
public static boolean DEBUG = false;
private static final String DEBUG_TAG = "ActivityMvpDelegateImpl";
private MvpDelegateCallback<V, P> delegateCallback;
protected boolean keepPresenterInstance;
protected Activity activity;
protected String mosbyViewId = null;
/**
* @param activity The Activity
* @param delegateCallback The callback
* @param keepPresenterInstance true, if the presenter instance should be kept across screen
* orientation changes. Otherwise false.
*/
public ActivityMvpDelegateImpl(@NonNull Activity activity,
@NonNull MvpDelegateCallback<V, P> delegateCallback, boolean keepPresenterInstance) {
if (activity == null) {
throw new NullPointerException("Activity is null!");
}
if (delegateCallback == null) {
throw new NullPointerException("MvpDelegateCallback is null!");
}
this.delegateCallback = delegateCallback;
this.activity = activity;
this.keepPresenterInstance = keepPresenterInstance;
}
/**
* Determines whether or not a Presenter Instance should be kept
*
* @param keepPresenterInstance true, if the delegate has enabled keep
*/
static boolean retainPresenterInstance(boolean keepPresenterInstance, Activity activity) {
return keepPresenterInstance && (activity.isChangingConfigurations()
|| !activity.isFinishing());
}
/**
* Generates the unique (mosby internal) view id and calls {@link
* MvpDelegateCallback#createPresenter()}
* to create a new presenter instance
*
* @return The new created presenter instance
*/
private P createViewIdAndCreatePresenter() {
P presenter = delegateCallback.createPresenter();
if (presenter == null) {
throw new NullPointerException(
"Presenter returned from createPresenter() is null. Activity is " + activity);
}
if (keepPresenterInstance) {
mosbyViewId = UUID.randomUUID().toString();
PresenterManager.putPresenter(activity, mosbyViewId, presenter);
}
return presenter;
}
@Override public void onCreate(Bundle bundle) {
P presenter = null;
if (bundle != null && keepPresenterInstance) {
mosbyViewId = bundle.getString(KEY_MOSBY_VIEW_ID);
if (DEBUG) {
Log.d(DEBUG_TAG,
"MosbyView ID = " + mosbyViewId + " for MvpView: " + delegateCallback.getMvpView());
}
if (mosbyViewId != null
&& (presenter = PresenterManager.getPresenter(activity, mosbyViewId)) != null) {
//
// Presenter restored from cache
//
if (DEBUG) {
Log.d(DEBUG_TAG,
"Reused presenter " + presenter + " for view " + delegateCallback.getMvpView());
}
} else {
//
// No presenter found in cache, most likely caused by process death
//
presenter = createViewIdAndCreatePresenter();
if (DEBUG) {
Log.d(DEBUG_TAG, "No presenter found although view Id was here: "
+ mosbyViewId
+ ". Most likely this was caused by a process death. New Presenter created"
+ presenter
+ " for view "
+ getMvpView());
}
}
} else {
//
// Activity starting first time, so create a new presenter
//
presenter = createViewIdAndCreatePresenter();
if (DEBUG) {
Log.d(DEBUG_TAG, "New presenter " + presenter + " for view " + getMvpView());
}
}
if (presenter == null) {
throw new IllegalStateException(
"Oops, Presenter is null. This seems to be a Mosby internal bug. Please report this issue here: https://github.com/sockeqwe/mosby/issues");
}
delegateCallback.setPresenter(presenter);
getPresenter().attachView(getMvpView());
if (DEBUG) {
Log.d(DEBUG_TAG, "View" + getMvpView() + " attached to Presenter " + presenter);
}
}
private P getPresenter() {
P presenter = delegateCallback.getPresenter();
if (presenter == null) {
throw new NullPointerException("Presenter returned from getPresenter() is null");
}
return presenter;
}
private V getMvpView() {
V view = delegateCallback.getMvpView();
if (view == null) {
throw new NullPointerException("View returned from getMvpView() is null");
}
return view;
}
@Override public void onDestroy() {
boolean retainPresenterInstance = retainPresenterInstance(keepPresenterInstance, activity);
getPresenter().detachView(retainPresenterInstance);
if (!retainPresenterInstance && mosbyViewId != null) {
PresenterManager.remove(activity, mosbyViewId);
}
if (DEBUG) {
if (retainPresenterInstance) {
Log.d(DEBUG_TAG, "View"
+ getMvpView()
+ " destroyed temporarily. View detached from presenter "
+ getPresenter());
} else {
Log.d(DEBUG_TAG, "View"
+ getMvpView()
+ " destroyed permanently. View detached permanently from presenter "
+ getPresenter());
}
}
}
@Override public void onPause() {
}
@Override public void onResume() {
}
@Override public void onStart() {
}
@Override public void onStop() {
}
@Override public void onRestart() {
}
@Override public void onContentChanged() {
}
@Override public void onSaveInstanceState(Bundle outState) {
if (keepPresenterInstance && outState != null) {
outState.putString(KEY_MOSBY_VIEW_ID, mosbyViewId);
if (DEBUG) {
Log.d(DEBUG_TAG,
"Saving MosbyViewId into Bundle. ViewId: " + mosbyViewId + " for view " + getMvpView());
}
}
}
@Override public void onPostCreate(Bundle savedInstanceState) {
}
}
代碼有點(diǎn)長,但是邏輯還是比較清晰的埃撵,它其實(shí)就是在 onCreate 方法中根據(jù)不同的情況來創(chuàng)建 Presenter 對(duì)象赵颅,并通過 MvpDelegateCallback 的 setPresenter 方法把它保存在 MvpDelegateCallback 中,這里的 MvpDelegateCallback 就是 MvpActivity 本身盯另。另外可以在 Activity 的各個(gè)生命周期方法中加入需要實(shí)現(xiàn)的邏輯性含。
可能有的同學(xué)會(huì)問,為什么沒有 Model 呢鸳惯?其實(shí)這里的代碼主要是對(duì) Presenter 的封裝商蕴,從作者給出的官方 demo 中可以發(fā)現(xiàn)叠萍,Model 和 View 都是需要自己創(chuàng)建的。
這里只做一個(gè)簡單的分析绪商,有興趣的同學(xué)可以自己查看它的源碼苛谷,再強(qiáng)調(diào)一遍,MVP 更多的是一種思想格郁,不用局限于某一種套路腹殿,可以在領(lǐng)悟了它的思想之后,寫出自己的 MVP例书。