一篇文章徹底搞懂 MVP

本文由玉剛說寫作平臺(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)的模型圖酌伊,如下

mvc

這個(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)說了始藕,下面看一下它的模型圖

mvp

這個(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例书。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末锣尉,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子决采,更是在濱河造成了極大的恐慌自沧,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件树瞭,死亡現(xiàn)場離奇詭異拇厢,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)晒喷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門孝偎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人凉敲,你說我怎么就攤上這事衣盾。” “怎么了荡陷?”我有些...
    開封第一講書人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵雨效,是天一觀的道長迅涮。 經(jīng)常有香客問我废赞,道長,這世上最難降的妖魔是什么叮姑? 我笑而不...
    開封第一講書人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任唉地,我火速辦了婚禮,結(jié)果婚禮上传透,老公的妹妹穿的比我還像新娘耘沼。我一直安慰自己,他們只是感情好朱盐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開白布群嗤。 她就那樣靜靜地躺著,像睡著了一般兵琳。 火紅的嫁衣襯著肌膚如雪狂秘。 梳的紋絲不亂的頭發(fā)上骇径,一...
    開封第一講書人閱讀 51,198評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音者春,去河邊找鬼破衔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛钱烟,可吹牛的內(nèi)容都是我干的晰筛。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼拴袭,長吁一口氣:“原來是場噩夢啊……” “哼读第!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起拥刻,我...
    開封第一講書人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤卦方,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后泰佳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體盼砍,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年逝她,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了浇坐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡黔宛,死狀恐怖近刘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情臀晃,我是刑警寧澤觉渴,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站徽惋,受9級(jí)特大地震影響案淋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜险绘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一踢京、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧宦棺,春花似錦瓣距、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春逻杖,著一層夾襖步出監(jiān)牢的瞬間慨默,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來泰國打工弧腥, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留厦取,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓管搪,卻偏偏與公主長得像虾攻,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子更鲁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,080評(píng)論 25 707
  • 作者:李旺成 時(shí)間:2016年4月3日 “Android MVP 詳解(下)”已經(jīng)發(fā)布霎箍,歡迎大家提建議。 MVP ...
    diygreen閱讀 128,868評(píng)論 86 1,321
  • 轉(zhuǎn)載至:http://www.reibang.com/p/9a6845b26856 “Android MVP 詳解...
    SnowDragonYY閱讀 10,322評(píng)論 5 241
  • 10月1號(hào)澡为,我們?cè)谌グ⒗朴⑿蹠?huì)現(xiàn)場的路中午我們吃的零食漂坏,下午我們到達(dá)磧口鎮(zhèn),晚上睡覺的時(shí)候在磧口客棧里睡的媒至,磧口...
    沈祺鴻閱讀 493評(píng)論 0 2
  • 2014拒啰,1月驯绎。距離高考還有5個(gè)多月,我在緊張的備考當(dāng)中谋旦,漫天飛舞的試卷剩失,老師苦口婆心的叮嚀囑咐卻依舊沒能抵擋得過...
    咚_閱讀 334評(píng)論 0 0