天天看點

Android MVP-程式設計思想2(代碼實作初級版)

前言

通過一個小的登入功能子產品案例,幫助大家了解MVP。最終實作一個結合Rxjava2,Retrofit 的MVP通用架構。代碼放到github上。(如有錯誤之處,請在評論區指出,謝謝。如果感覺寫的不錯,請點贊,關注,謝謝。)

目錄:

Android MVP-程式設計思想1(什麼是MVC-MVP-MVVM模式?)

Android MVP-程式設計思想2(代碼實作初級版)

Android MVP-程式設計思想3(記憶體洩露問題處理,基類封裝,有沒有必要再使用軟引用?)

Android MVP-程式設計思想4(AOP思想-動态代理運用,反射建立M層執行個體對象)

Android MVP-程式設計思想5(如何處理多個P層的問題?)

Android MVP-程式設計思想6(依賴注入多個P層優化—注解,反射)

Android MVP-程式設計思想7(為什麼使用代理類抽取通用方法而不是工具類?,基類BaseMvpFragment)

未完待續--------

Android MVP-程式設計思想8(內建Rxjava2,Retrofit)

MVP 實戰

那麼我們下面就要将這個類中的代碼改寫為 MVP 的寫法,回顧上面提及的 MVP 架構的思想,它是将 View 層與 Model 層徹底隔離,意味着 View 和 Model 都不再持對方的引用,它們通過一個第三者 Presenter 來代理事物的傳遞,是以 Presenter 層會持有 Model 與 View 層的引用,這是第一步。

第二步,是将它們之間的聯系抽象出來,以接口的方式互相調用,是以 Model 、View、Presenter 各自擁有自己的接口和抽象方法,是以這就會無形的多出了三個接口類,這也就是 MVP 的缺點之一。是以,為了較少的建立接口類,我們就給這三層接口定義了一個契約接口,把它們更加緊密的結合在一起,方法檢視,例如代碼這樣寫:參考谷歌官方mvp案例todo-mvp

下面我們以登入功能為例行了解 MVP 架構的代碼寫法。

第一步

首先我們參考谷歌todo-mvp 寫一個契約類,定義互動行為。契約類的目的是把MVP的行為定義放在一起,友善閱讀和維護

public class LoginContract {
    /**
     * V 定義View的行為,調用者是P
     */
    interface IView {
        //登入成功跳轉到主界面
        void goToMainActivity();

        //請求網絡顯示等待框
        void showProgress(boolean isShow);

        //顯示提示框-登入失敗,或其他提示資訊
        void showToast(String string);
    }

    /**
     * P(中轉站和資料處理者) 定義View與Model的互動行為,調用者是V。P做中轉
     */
    interface IPresenter {
        void onClickLogin(String username, String pwd);
    }

    /**
     * M 資料提供者
     */
    interface IModel {
        void getUserInfo(String username, String pwd, ICallBack callBack);
    }

}
           
第二步,定義MVP行為接口的實作類

輔助類ICallBack,用來回調http請求的資料

public interface ICallBack<T> {
    void success(T data);

    void error(String error);
}
           

M層的實作類LoginModel,提供資料,網絡資料,本地資料

public class LoginModel implements LoginContract.IModel {
    @Override
    public void getUserInfo(String username, String pwd, final ICallBack callBack) {
        //todo 模拟 http請求
        Handler handler = new Handler();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                callBack.success("成功");
            }
        };
        handler.postDelayed(runnable, 2000);

    }
}
           

V層的實作類LoginActivity,控制視圖顯示,要調用P層接口,是以要持有P對象的引用

public class LoginActivity extends AppCompatActivity implements LoginContract.IView {
    LoginContract.IPresenter mPresenter;

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

        mPresenter = new LoginPresenter(this);
        findViewById(R.id.btn_login).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mPresenter.onClickLogin("xxx", "xxx");
            }
        });
    }

    @Override
    public void goToMainActivity() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                //todo 登入成功跳轉到主界面
                Log.i("mvp_", "登入成功跳轉到主界面");
            }
        });

    }

    @Override
    public void showProgress(boolean isShow) {
        if (isShow) {
            Log.i("mvp_", "顯示等待框");
        } else {
            Log.i("mvp_", "隐藏等待框");
        }
    }

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

P層實作類LoginPresenter,是M和V互動的協調者,是以要持有M,V對象的引用

注意點:網絡請求是耗時操作,我們 Presenter 層持有了 View 層的引用,也就是 Activity 的引用,在網絡請求過程中,Activity被使用者或者系統關閉,這時 View 相當于被摧毀了,如果不進行判斷 View 引用是否為空,當資料傳回時,就會造成空指針異常,是以每次都要進行判空才合理。

public class LoginPresenter implements LoginContract.IPresenter {
    private LoginContract.IModel mModel;
    private LoginContract.IView mView;

    public LoginPresenter(LoginContract.IView view) {
        this.mModel = new LoginModel();
        this.mView = view;
    }

    @Override
    public void onClickLogin(String username, String pwd) {
        mView.showProgress(true);
        mModel.getUserInfo(username, pwd, new ICallBack<String>() {
            @Override
            public void success(String data) {
                if (mView != null) {
                    mView.showToast("登入成功");
                    mView.showProgress(false);
                    mView.goToMainActivity();
                }
            }

            @Override
            public void error(String error) {
                if (mView != null) {
                    mView.showProgress(false);
                    mView.showToast("登入失敗");
                }
            }
        });
    }
}

           

上面使用最簡單的方式,示範了MVP的設計思想。目的是讓大家了解,MVP模式是如何把業務邏輯,資料與V層進行分離的。再回想對比MVC是不是覺得MVP模式,職責分離的更清楚。

運作,點選登入按鈕,列印日志如下:---------------------

2019-12-11 14:34:06.990 6376-6376/chongchong.wei.rx_retrofit_mvp I/mvp_: 顯示等待框

2019-12-11 14:34:09.002 6376-6376/chongchong.wei.rx_retrofit_mvp I/mvp_: 隐藏等待框

2019-12-11 14:34:09.002 6376-6376/chongchong.wei.rx_retrofit_mvp I/mvp_: 登入成功跳轉到主界面

當然上面的代碼隻是幫助大家了解MVP模式的思想,一些細節問題沒有處理。下一節我們對代碼進行優化,抽離,封裝。