Android MVP升級路(一)乞丐版的自我救贖

目錄

  • 引言
  • 為什么用MVP架構
  • MVP理論知識
  • 乞丐版MVP架構模式的代碼實現(xiàn)
  • MVP中的代碼復用場景
  • 平民版MVP架構 - base層頂級父類
  • Fragment怎么辦
  • 時尚版MVP架構 - Model層的單獨優(yōu)化

引言

記得第一次接觸MVP開發(fā)是上大學的時候,當時看了數(shù)十篇關于MVP的文章,這里不得不吐槽一下國內技術帖子的質量真是參次不齊啊??赐曛笠恢便裸露模傆X有幾處關鍵的地方?jīng)]搞清但是文章中卻一帶而過了,比如:

  • 關于如何在Activity中高效的復用Presenter和View;
  • Mode層定義到什么程度才算是比較理想的解耦;
  • Model層與Presenter層如何比較優(yōu)雅的相互通信。

抱著這些問題,我自己摸索著構建出了一套個性化風格MVP架構,使用過程中也優(yōu)化了幾次,如今一年多過去了再看這套架構也就算是個能用吧,所以決定新的架構優(yōu)化。

本文講述了MVP的核心概念和如何從最初的乞丐版MVP架構一步步升級到平民版MVP架構,時尚版MVP架構,以及即將開始更新的旗艦版MVP架構,為了保證思路清晰,文中包含大量代碼與文字,跟著文中的例子便可寫出一個完整的MVP架構。

為什么用MVP架構

其實我們日常開發(fā)中的Activity,F(xiàn)ragment和XML界面就相當于是一個 MVC 的架構模式,Activity中不僅要處理各種 UI 操作還要請求數(shù)據(jù)以及解析。

這種開發(fā)方式的缺點就是業(yè)務量大的時候一個Activity 文件分分鐘飆到上千行代碼,想要改一處業(yè)務邏輯光是去找就要費半天勁,而且有點地方邏輯處理是一樣的無奈是不同的 Activity 就沒辦法很好的寫成通用方法。

那 MVP 為啥好用呢?

MVP 模式將Activity 中的業(yè)務邏輯全部分離出來,讓Activity 只做 UI 邏輯的處理,所有跟Android API無關的業(yè)務邏輯由 Presenter 層來完成。

將業(yè)務處理分離出來后最明顯的好處就是管理方便,但是缺點就是增加了代碼量。

MVP 理論知識

在MVP 架構中跟MVC類似的是同樣也分為三層。

Activity 和Fragment 視為View層,負責處理 UI。

Presenter 為業(yè)務處理層,既能調用UI邏輯,又能請求數(shù)據(jù),該層為純Java類,不涉及任何Android API。

Model 層中包含著具體的數(shù)據(jù)請求,數(shù)據(jù)源。

三層之間調用順序為view->presenter->model,為了調用安全著想不可反向調用!不可跨級調用!

那Model 層如何反饋給Presenter 層的呢?Presenter 又是如何操控View 層呢?看圖!

MVP架構調用關系

上圖中說明了低層的不會直接給上一層做反饋,而是通過 View 、 Callback 為上級做出了反饋,這樣就解決了請求數(shù)據(jù)與更新界面的異步操作。上圖中 View 和 Callback 都是以接口的形式存在的,其中 View 是經(jīng)典 MVP 架構中定義的,Callback 是我自己加的。

View 中定義了 Activity 的具體操作,主要是些將請求到的數(shù)據(jù)在界面中更新之類的。

Callback 中定義了請求數(shù)據(jù)時反饋的各種狀態(tài):成功、失敗、異常等。

乞丐版MVP架構模式的代碼實現(xiàn)

下面我們用 MVP 模式構造一個簡易模擬請求網(wǎng)絡的小程序。效果圖如下:

成功加載到數(shù)據(jù)
加載數(shù)據(jù)失敗
數(shù)據(jù)獲取異常

因為是模擬網(wǎng)絡數(shù)據(jù)請求,所以有三個請求數(shù)據(jù)的按鈕分別對應成功、失敗、異常三種不同的反饋狀態(tài)。

下面是Demo中的Java文件目錄:

Java文件目錄

Callback接口

Callback 接口是Model層給Presenter層反饋請求信息的傳遞載體,所以需要在Callback中定義數(shù)據(jù)請求的各種反饋狀態(tài):

public interface MvpCallback {

   /**
     * 數(shù)據(jù)請求成功
     * @param data 請求到的數(shù)據(jù)
     */
    void onSuccess(String data);

    /**
     *  使用網(wǎng)絡API接口請求方式時,雖然已經(jīng)請求成功但是由
     *  于{@code msg}的原因無法正常返回數(shù)據(jù)。
     */
    void onFailure(String msg);

     /**
     * 請求數(shù)據(jù)失敗,指在請求網(wǎng)絡API接口請求方式時,出現(xiàn)無法聯(lián)網(wǎng)、
     * 缺少權限,內存泄露等原因導致無法連接到請求數(shù)據(jù)源。
     */
    void onError();

    /**
     * 當請求數(shù)據(jù)結束時,無論請求結果是成功,失敗或是拋出異常都會執(zhí)行此方法給用戶做處理,通常做網(wǎng)絡
     * 請求時可以在此處隱藏“正在加載”的等待控件
     */
    void onComplete();

}

Model 類

Model 類中定了具體的網(wǎng)絡請求操作。為模擬真實的網(wǎng)絡請求,利用postDelayed方法模擬耗時操作,通過判斷請求參數(shù)反饋不同的請求狀態(tài):

public class MvpModel {

    /**
     * 獲取網(wǎng)絡接口數(shù)據(jù)
     * @param param 請求參數(shù)
     * @param callback 數(shù)據(jù)回調接口
     */
    public static void getNetData(final String param, final MvpCallback callback){
        
        // 利用postDelayed方法模擬網(wǎng)絡請求數(shù)據(jù)的耗時操作
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                switch (param){

                    case "normal":
                        callback.onSuccess("根據(jù)參數(shù)"+param+"的請求網(wǎng)絡數(shù)據(jù)成功");
                        break;

                    case "failure":
                        callback.onFailure("請求失敗:參數(shù)有誤");
                        break;

                    case "error":
                        callback.onError();
                        break;
                }
                callback.onComplete();
            }

        },2000);
    }

}

View 接口

View接口是Activity與Presenter層的中間層,它的作用是根據(jù)具體業(yè)務的需要,為Presenter提供調用Activity中具體UI邏輯操作的方法。

public interface MvpView {

    /**
     * 顯示正在加載進度框
     */
    void showLoading();

    /**
     * 隱藏正在加載進度框
     */
    void hideLoading();

    /**
     * 當數(shù)據(jù)請求成功后,調用此接口顯示數(shù)據(jù)
     * @param data 數(shù)據(jù)源
     */
    void showData(String data);

    /**
     * 當數(shù)據(jù)請求失敗后,調用此接口提示
     * @param msg 失敗原因
     */
    void showFailureMessage(String msg);

    /**
     * 當數(shù)據(jù)請求異常,調用此接口提示
     */
    void showErrorMessage();

}

Presenter類

Presenter類是具體的邏輯業(yè)務處理類,該類為純Java類,不包含任何Android API,負責請求數(shù)據(jù),并對數(shù)據(jù)請求的反饋進行處理。

Presenter類的構造方法中有一個View接口的參數(shù),是為了能夠通過View接口通知Activity進行更新界面等操作。

public class MvpPresenter {

    // View接口
    private MvpView mView;


    public MvpPresenter(MvpView view){
        this.mView = view;
    }

    /**
     * 獲取網(wǎng)絡數(shù)據(jù)
     * @param params 參數(shù)
     */
    public void getData(String params){

        //顯示正在加載進度條
        mView.showLoading();
        // 調用Model請求數(shù)據(jù)
        MvpModel.getNetData(params, new MvpCallback() {
            @Override
            public void onSuccess(String data) {
                //調用view接口顯示數(shù)據(jù)
                mView.showData(data);
            }

            @Override
            public void onFailure(String msg) {
                //調用view接口提示失敗信息
                mView.showFailureMessage(msg);
            }

            @Override
            public void onError() {
                //調用view接口提示請求異常
                mView.showErrorMessage();
            }

            @Override
            public void onComplete() {
                // 隱藏正在加載進度條
                mView.hideLoading();
            }
        });
    }

}

xml布局文件

沒什么好說的,直接上代碼:

<?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:padding="16dp"
    android:orientation="vertical"
    tools:context="com.jessewu.mvpdemo.MainActivity">


    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:text="點擊按鈕獲取網(wǎng)絡數(shù)據(jù)"/>


    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="獲取數(shù)據(jù)【成功】"
        android:onClick="getData"
        />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="獲取數(shù)據(jù)【失敗】"
        android:onClick="getDataForFailure"
        />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="獲取數(shù)據(jù)【異?!?
        android:onClick="getDataForError"
        />

</LinearLayout>

Activity

在Activity代碼中需要強調的是如果想要調用Presenter就要先實現(xiàn)Presenter需要的對應的View接口。

public class MainActivity extends AppCompatActivity implements MvpView  {

    //進度條
    ProgressDialog progressDialog;
    TextView text;

    MvpPresenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        text = (TextView)findViewById(R.id.text);

        // 初始化進度條
        progressDialog = new ProgressDialog(this);
        progressDialog.setCancelable(false);
        progressDialog.setMessage("正在加載數(shù)據(jù)");

        //初始化Presenter
        presenter = new MvpPresenter(this);

    }

    // button 點擊事件調用方法
    public void getData(View view){
        presenter.getData("normal");
    }

    // button 點擊事件調用方法
    public void getDataForFailure(View view){
        presenter.getData("failure");
    }

    // button 點擊事件調用方法
    public void getDataForError(View view){
        presenter.getData("error");
    }

    @Override
    public void showLoading() {
        if (!progressDialog.isShowing()) {
            progressDialog.show();
        }
    }

    @Override
    public void hideLoading() {
        if (progressDialog.isShowing()) {
            progressDialog.dismiss();
        }
    }

    @Override
    public void showData(String data) {
        text.setText(data);
    }

    @Override
    public void showFailureMessage(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
        text.setText(msg);
    }

    @Override
    public void showErrorMessage() {
        Toast.makeText(this, "網(wǎng)絡請求數(shù)據(jù)出現(xiàn)異常", Toast.LENGTH_SHORT).show();
        text.setText("網(wǎng)絡請求數(shù)據(jù)出現(xiàn)異常");
    }
}

至此,已經(jīng)完整的實現(xiàn)了一個簡易的MVP架構。

注意!以上代碼中還存在很大的問題,不能用到實際開發(fā)中,下面會討論目前存在的問題以及如何優(yōu)化。

MVP中的代碼復用場景

因為上節(jié)中乞丐版MVP Demo的代碼只實現(xiàn)了一個Activity的請求操作,容易出現(xiàn)一個概念的混淆:

每個Activity都需要有與它對應的一套MVP(Model,View,Presenter)嗎?

答案肯定是否定的!

首先不需要數(shù)據(jù)請求的Activity當然就同樣不需要MVP輔助。與其他Activity中存在相同邏輯的Activity,就不需要重復生成對應的MVP。但是這個存在相同邏輯的定義,不同的場景有不同的說法:

需求.jpg

場景1:業(yè)務邏輯完全相同

場景1中Activity A和Activity C都只有一個“買東西”的邏輯,屬于典型的邏輯相同,所以Activity C就可以直接用Activity A寫好的MVP無需再做任何處理。

場景2、3:包含部分相同業(yè)務邏輯

場景2和場景3的邏輯類似,都屬于一個業(yè)務邏輯中包含另外一個可以單獨存在的業(yè)務邏輯,這種情況采用繼承的方法即可:

繼承關系.png

場景4

場景4中Activity C想要同時調用獨立服務于Activity A 和 Activity B的業(yè)務邏輯,只需要將兩個業(yè)務邏輯對應的Presenter分別實例化并調用業(yè)務方法即可:

private PresenterA presenterA;
private PresenterB presenterB;

...
...

private void getData(){
    presenterA.getData();
    presenterB.getData();
}

不要忘了實現(xiàn)兩個Presenter對應的View:

public class ActivityC extends Activity implements ViewA,ViewB{
  ...
}

場景5

場景5屬于場景3與場景4的結合體,同樣需要先把A和B的業(yè)務邏輯拆分開,然后同時調用,這里就不舉例子了。

總結

通過上面一攬子場景的分析,得出的第一個結論就是MVP的結構太過于繁重,所以為了避免多寫重復代碼和日后需要進行無意義的修改,在開發(fā)前一定要設計好邏輯調用圖,這樣才能事半功倍。

對于上面經(jīng)典的通過業(yè)務邏輯繼承實現(xiàn)包含重復邏輯的方法,其實也可以在一個Presenter中寫好完整的邏輯方法,對于不同的Activity需要哪個業(yè)務邏輯方法就調用哪個,這樣豈不就簡單多了。但是從架構設計角度看這種做法是不嚴謹?shù)?,可能存在漏洞,所以為保持軟件架構的健壯還是不要偷懶的好。

平民版MVP架構 - base層頂級父類

之前說過乞丐版MVP架構模式中還存在很多問題不能應用到實際的開發(fā)中,大概存在的問題有:

  • 構架存在漏洞
  • 代碼冗余量大
  • 通用性差

針對這些問題我們需要進一步優(yōu)化,單車變摩托,升級為可以在實際開發(fā)中使用的平民版MVP架構。

調用View可能引發(fā)的空指針異常

舉一個例子,在上述乞丐版MVP架構中的應用請求網(wǎng)絡數(shù)據(jù)時需要等待后臺反饋數(shù)據(jù)后更新界面,但是在請求過程中當前Activity突然因為某種原因被銷毀,Presenter收到后臺反饋并調用View接口處理UI邏輯時由于Activity已經(jīng)被銷毀,就會引發(fā)空指針異常。

想要避免這種情況的發(fā)生就需要每次調用View前都知道宿主Activity的生命狀態(tài)。

之前是在Presenter的構造方法中得到View接口的引用,現(xiàn)在我們需要修改Presenter引用View接口的方式讓View接口與宿主Activity共存亡:

public class MvpPresenter {

    // View接口
    private MvpView mView;


    public MvpPresenter(){
        //構造方法中不再需要View參數(shù)
    }

   /**
     * 綁定view,一般在初始化中調用該方法
     */
    public void attachView(MvpView  mvpView) {
        this.mView= mvpView;
    }

    /**
     * 斷開view,一般在onDestroy中調用
     */
    public void detachView() {
        this.mView= null;
    }

    /**
     * 是否與View建立連接
     * 每次調用業(yè)務請求的時候都要出先調用方法檢查是否與View建立連接
     */
    public boolean isViewAttached(){
        return mView!= null;
    }


    /**
     * 獲取網(wǎng)絡數(shù)據(jù)
     * @param params 參數(shù)
     */
    public void getData(String params){

        //顯示正在加載進度條
        mView.showLoading();
        // 調用Model請求數(shù)據(jù)
        MvpModel.getNetData(params, new MvpCallback() {
            @Override
            public void onSuccess(String data) {
                //調用view接口顯示數(shù)據(jù)
                if(isViewAttached()){
                    mView.showData(data);
                }
                
            }

            @Override
            public void onFailure(String msg) {
                //調用view接口提示失敗信息
                if(isViewAttached()){
                     mView.showFailureMessage(msg);
                }          
            }

            @Override
            public void onError() {
                //調用view接口提示請求異常
                if(isViewAttached()){
                    mView.showErrorMessage();
                } 
            }

            @Override
            public void onComplete() {
                // 隱藏正在加載進度條
                if(isViewAttached()){
                    mView.hideLoading();
                } 
            }
        });
    }

}

上面Presenter代碼中比之前增加了三個方法:

  • attachView() 綁定View引用。
  • detachView 斷開View引用。
  • isViewAttached() 判斷View引用是否存在。

其中attachView()detachView()是為Activity準備的,isViewAttached()作用是Presenter內部每次調用View接口中的方法是判斷View 的引用是否存在。

把綁定View的方法寫到Activity的生命周期中:

public class MainActivity extends Activity implements MvpView{

  MvpPresenter presenter;

  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //初始化Presenter
        presenter = new MvpPresenter();
        // 綁定View引用
        presenter.attachView(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 斷開View引用
        presenter.detachView();
    }

}

結合Activity構建base層

寫到這里,相信大多數(shù)人都會驚訝于MVP模式代碼量的巨大,冗余代碼實在太多,所以接下需要為MVP中所有單元都設計一個頂級父類來減少重復的冗余代碼。同樣的道理,我們也為Activity設計一個父類方便與MVP架構更完美的結合。最后將所有父類單獨分到一個base包中供外界繼承調用。

Callback

在乞丐版中Callback接口中的onSuccess()方法需要根據(jù)請求數(shù)據(jù)類型的不同設置為不同類型的參數(shù),所以每當有新的數(shù)據(jù)類型都需要新建一個Callback,解決方法是引入泛型的概念,用調用者去定義具體想要接收的數(shù)據(jù)類型:

public interface Callback<T> {

   /**
     * 數(shù)據(jù)請求成功
     * @param data 請求到的數(shù)據(jù)
     */
    void onSuccess(T data);

    /**
     *  使用網(wǎng)絡API接口請求方式時,雖然已經(jīng)請求成功但是由
     *  于{@code msg}的原因無法正常返回數(shù)據(jù)。
     */
    void onFailure(String msg);

     /**
     * 請求數(shù)據(jù)失敗,指在請求網(wǎng)絡API接口請求方式時,出現(xiàn)無法聯(lián)網(wǎng)、
     * 缺少權限,內存泄露等原因導致無法連接到請求數(shù)據(jù)源。
     */
    void onError();

    /**
     * 當請求數(shù)據(jù)結束時,無論請求結果是成功,失敗或是拋出異常都會執(zhí)行此方法給用戶做處理,通常做網(wǎng)絡
     * 請求時可以在此處隱藏“正在加載”的等待控件
     */
    void onComplete();

}

BaseView

View接口中定義Activity的UI邏輯。因為有很多方法幾乎在每個Activity中都會用到,例如顯示和隱藏正在加載進度條,顯示Toast提示等,索性將這些方法變成通用的:

public interface BaseView {
   
     /**
     * 顯示正在加載view
     */
    void showLoading();

    /**
     * 關閉正在加載view
     */
    void hideLoading();

    /**
     * 顯示提示
     * @param msg
     */
    void showToast(String msg);

    /**
     * 顯示請求錯誤提示
     */
    void showErr();

    /**
     * 獲取上下文
     * @return 上下文
     */
    Context getContext();
}

BasePresenter

Presenter中可共用的代碼就是對View引用的方法了,值得注意的是,上面已經(jīng)定義好了BaseView,所以我們希望Presenter中持有的View都是BaseView的子類,這里同樣需要泛型來約束:

public class BasePresenter<V extends BaseView> {

    /**
     * 綁定的view
     */
    private V mvpView;

    /**
     * 綁定view,一般在初始化中調用該方法
     */
    @Override
    public void attachView(V mvpView) {
        this.mvpView = mvpView;
    }

    /**
     * 斷開view,一般在onDestroy中調用
     */
    @Override
    public void detachView() {
        this.mvpView = null;
    }

    /**
     * 是否與View建立連接
     * 每次調用業(yè)務請求的時候都要出先調用方法檢查是否與View建立連接
     */
    public boolean isViewAttached(){
        return mvpView != null;
    }

    /**
     * 獲取連接的view
     */
    public V getView(){
        return mvpView;
    }

BaseActivity

BaseActivity主要是負責實現(xiàn) BaseView 中通用的UI邏輯方法,如此這些通用的方法就不用每個Activity都要去實現(xiàn)一遍了。

public abstract class BaseActivity extends Activity implements BaseView {

    private ProgressDialog mProgressDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mProgressDialog = new ProgressDialog(this);
        mProgressDialog.setCancelable(false);
    }

    @Override
    public void showLoading() {
        if (!mProgressDialog.isShowing()) {
            mProgressDialog.show();
        }
    }

    @Override
    public void hideLoading() {
        if (mProgressDialog.isShowing()) {
            mProgressDialog.dismiss();
        }
    }

    @Override
    public void showToast(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void showErr() {
        showToast(getResources().getString(R.string.api_error_msg));
    }

    @Override
    public Context getContext() {
        return BaseActivity.this;
    }

平民版MVP架構代碼實現(xiàn)

封裝好了base層我們的平民版MVP架構就完成了,下面再來實現(xiàn)一遍之前用乞丐版MVP實現(xiàn)的應用。

Model

public class MvpModel {

    /**
     * 獲取網(wǎng)絡接口數(shù)據(jù)
     * @param param 請求參數(shù)
     * @param callback 數(shù)據(jù)回調接口
     */
    public static void getNetData(final String param, final MvpCallback<String> callback){
        
        // 利用postDelayed方法模擬網(wǎng)絡請求數(shù)據(jù)的耗時操作
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                switch (param){

                    case "normal":
                        callback.onSuccess("根據(jù)參數(shù)"+param+"的請求網(wǎng)絡數(shù)據(jù)成功");
                        break;

                    case "failure":
                        callback.onFailure("請求失?。簠?shù)有誤");
                        break;

                    case "error":
                        callback.onError();
                        break;
                }
                callback.onComplete();
            }

        },2000);
    }

}

View 接口

public interface MvpView extends BaseView{
    /**
     * 當數(shù)據(jù)請求成功后,調用此接口顯示數(shù)據(jù)
     * @param data 數(shù)據(jù)源
     */
    void showData(String data);

}

Presenter類

public class MvpPresenter extends BasePresenter<MvpView > {


    /**
     * 獲取網(wǎng)絡數(shù)據(jù)
     * @param params 參數(shù)
     */
    public void getData(String params){
        
        if (!isViewAttached()){
            //如果沒有View引用就不加載數(shù)據(jù)
            return;
        }

        //顯示正在加載進度條
        getView().showLoading();

        // 調用Model請求數(shù)據(jù)
        MvpModel.getNetData(params, new MvpCallback()<String> {
            @Override
            public void onSuccess(String data) {
                //調用view接口顯示數(shù)據(jù)
                if(isViewAttached()){
                    getView().showData(data);
                }
            }

            @Override
            public void onFailure(String msg) {
                //調用view接口提示失敗信息
                if(isViewAttached()){
                     getView().showToast(msg);
                }          
            }

            @Override
            public void onError() {
                //調用view接口提示請求異常
                if(isViewAttached()){
                    getView().showErr();
                } 
            }

            @Override
            public void onComplete() {
                // 隱藏正在加載進度條
                if(isViewAttached()){
                    getView().hideLoading();
                } 
            }
        });
    }

}

Activity

public class MainActivity extends BaseActivity implements MvpView  {

    TextView text;

    MvpPresenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        text = (TextView)findViewById(R.id.text);

        //初始化Presenter
        presenter = new MvpPresenter();
        presenter.attachView(this);
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //斷開View引用
        presenter.detachView();
    }

    @Override
    public void showData(String data) {
        text.setText(data);
    }

    // button 點擊事件調用方法
    public void getData(View view){
        presenter.getData("normal");
    }

    // button 點擊事件調用方法
    public void getDataForFailure(View view){
        presenter.getData("failure");
    }

    // button 點擊事件調用方法
    public void getDataForError(View view){
        presenter.getData("error");
    }  
}

Fragment怎么辦?

日常開發(fā)中,并不是所有的UI處理都在Activity中進行,F(xiàn)ragment也是其中很重要的一員,那么如何將Fragment結合到MVP中呢?

實現(xiàn)BaseFragement做法跟BaseActivity很類似,需要注意一下Fragement與宿主Activity的鏈接情況就可以。

public abstract class BaseFragment extends Fragment implements BaseView {

    public abstract int getContentViewId();

    protected abstract void initAllMembersView(Bundle savedInstanceState);

    protected Context mContext;
    protected View mRootView;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        mRootView = inflater.inflate(getContentViewId(), container, false);
        this.mContext = getActivity();
        initAllMembersView(savedInstanceState);
        return mRootView;
    }

    @Override
    public void showLoading() {
        checkActivityAttached();
        ((BaseFragmentActivity) mContext).showLoading();
    }

    @Override
    public void showLoading(String msg) {
        checkActivityAttached();
        ((BaseFragmentActivity) mContext).showLoading(msg);
    }

    @Override
    public void hideLoading() {
        checkActivityAttached();
        ((BaseFragmentActivity) mContext).hideLoading();
    }

    @Override
    public void showToast(String msg) {
        checkActivityAttached();
        ((BaseFragmentActivity) mContext).showToast(msg);
    }

    @Override
    public void showErr() {
        checkActivityAttached();
        ((BaseFragmentActivity) mContext).showErr();
    }

    protected boolean isAttachedContext(){
        return getActivity() != null;
    }

    /**
     * 檢查activity連接情況
     */
    public void checkActivityAttached() {
        if (getActivity() == null) {
            throw new ActivityNotAttachedException();
        }
    }

    public static class ActivityNotAttachedException extends RuntimeException {
        public ActivityNotAttachedException() {
            super("Fragment has disconnected from Activity ! - -.");
        }
    }

}

時尚版MVP架構 - Model層的單獨優(yōu)化

在從乞丐版MVP架構優(yōu)化成平民版MVP架構的過程中,幾乎每個單元都做了很大優(yōu)化并封裝到了base層,但是唯獨Model層沒什么變化。所以,時尚版MVP架構的優(yōu)化主要就是對Model層的優(yōu)化。

Model層相比其他單元來說比較特殊,因為它們更像一個整體,只是單純的幫上層拿數(shù)據(jù)而已。再就是MVP的理念是讓業(yè)務邏輯互相獨立,這就導致每個的網(wǎng)絡請求也被獨立成了單個Model,不光沒必要這么做而且找起來賊麻煩,所以時尚版MVP架構中Model層被整體封裝成了龐大且獨立單一模塊。

優(yōu)化之后的Model層是一個龐大而且獨立的模塊,對外提供統(tǒng)一的請求數(shù)據(jù)方法與請求規(guī)則,這樣做的好處有很多:

  • 數(shù)據(jù)請求單獨編寫,無需配合上層界面測試。
  • 統(tǒng)一管理,修改方便。
  • 實現(xiàn)不同數(shù)據(jù)源(NetAAPI,cache,database)的無縫切換。

寫到這里本片文章實在是太長了,所以時尚版MVP架構的實現(xiàn)就留到下片文章繼續(xù)把。

Android MVP升級路(二)時尚版

未完待續(xù)

下篇會完善時尚版MVP架構,以及最新的旗艦版MVP架構設計,敬請期待~

旗艦版MVP架構圖

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容