本文由玉剛說寫作平臺提供寫作贊助,版權(quán)歸玉剛說微信公眾號所有
原作者: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è)對 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ì)請求網(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 對象,并把 TextView 對象作為參數(shù)傳入,然后調(diào)用它的 request 方法來請求數(shù)據(jù),當(dā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 對象并調(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 請求數(shù)據(jù),Model 獲取到數(shù)據(jù)之后通知 Presenter,Presenter 再通知 View 更新數(shù)據(jù),這樣 Model 和 View 就不會(huì)直接交互了,所有的交互都由 Presenter 進(jìn)行,Presenter 充當(dāng)了橋梁的角色。很顯然,Presenter 必須同時(shí)持有 View 和 Model 的對象的引用,才能在它們之間進(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ò)請求獲取數(shù)據(jù)是異步的,在獲取到數(shù)據(jù)之后需要通過 Callback 來通知 Presenter。Presenter 也很簡單,首先在它的構(gòu)造方法中,同時(shí)持有 View 和 Model 的引用,再對外提供一個(gè) request 方法。
分析一下上面代碼執(zhí)行的流程,當(dāng)點(diǎn)擊 Button 的時(shí)候,Presenter 調(diào)用 request 方法,在它的內(nèi)部,通過 Model 調(diào)用 request 方法來請求數(shù)據(jù),請求到數(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è)針對 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 的對象無法被回收,造成了內(nèi)存泄漏,解決的方式很簡單,在 Activity 退出的時(shí)候,把 Presenter 對中 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 中請求數(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è)對應(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)引用他們的對象了。Presenter 中的通用的方法主要就是 attachView 和 detachView,分別用于創(chuàng)建 View 對象和把 View 的對象置位空,前面已經(jīng)說過,置空是為了防止內(nèi)存泄漏,Model 的對象可以在 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 就對應(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 對象,它的實(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 對象,并通過 MvpDelegateCallback 的 setPresenter 方法把它保存在 MvpDelegateCallback 中,這里的 MvpDelegateCallback 就是 MvpActivity 本身。另外可以在 Activity 的各個(gè)生命周期方法中加入需要實(shí)現(xiàn)的邏輯。
可能有的同學(xué)會(huì)問,為什么沒有 Model 呢?其實(shí)這里的代碼主要是對 Presenter 的封裝,從作者給出的官方 demo 中可以發(fā)現(xiàn),Model 和 View 都是需要自己創(chuàng)建的。
這里只做一個(gè)簡單的分析,有興趣的同學(xué)可以自己查看它的源碼,再強(qiáng)調(diào)一遍,MVP 更多的是一種思想,不用局限于某一種套路,可以在領(lǐng)悟了它的思想之后,寫出自己的 MVP。