歡迎光臨
每天分享高質量文章

Android MVP 架構

作者:菜鳥考官
連結:https://www.jianshu.com/p/ae0b21d3238a

本Demo使用 Okhttp3、Retrofit2、Rxjava2 ,使用AutoDispose解決RxJava記憶體洩漏

Github:
https://github.com/RookieExaminer/MvpDemo

什麼是 MVP,為什麼要用MVP?

網上介紹MVP的很多,百度一下你就知道,至於為什麼要用MVP,當然是由於它的優勢了:

1、程式碼簡潔

此處的簡潔是邏輯的簡潔,而不是程式碼本身 舉個慄子

image.png

比如購物車介面,有很多請求網路的地方:獲取購物車商品串列、購物車商品的刪除、購物車商品的購買等等, 這麼多網路請求,如果都寫在一個 Activity,而且還有大量邏輯判斷,那這個 Activity的行數~ 看著就讓人頭痛, 即便寫了註釋,維護起來也是比較麻煩的

2.降低耦合,方便維護

MVP的使用,使Activity中的網路請求剝離出來 成為model、presenter,model只負責網路的請求、pesenter負責處理請求網路後的資料處理:載入中 成功 or 失敗 取消載入;最後View進行介面的展示

image.png
image.png

Start 看圖:

image.png

嗯哼? 不是 Model、Presenter、View這三個 麼,怎麼又多出來個Contract,這又是什麼鬼?

這就涉及到MVP的缺點了,正所謂,金無足赤,人無完人,MVP既然有優點當然也有它的缺點了

MVP在實現程式碼簡潔的同時,額外增加了大量的介面、類,不方便進行管理,於是Contract 就登場了。

Contract 百度翻譯 : 合同;契約;協議
Contract 如其名,是一個契約,將 Model、View、Presenter 進行約束管理,方便後期類的查詢、維護。

下麵演示下登陸的MVP實現方式

(示例程式碼由開發專案中剝離到Demo中,登陸介面使用的是玩安卓的登陸API:http://www.wanandroid.com/blog/show/2)

首先,建立一個登陸的Contract:

public interface MainContract {
    interface Model { }

    interface View extends BaseView { }

    interface Presenter { }
}

其次建立 Presenter、Model、View 對應 Contract 中的介面;

public class MainPresenter implements MainContract.Model{}
public class MainModel implements MainContract.Presenter{}
public class MainActivity  implements MainContract.View {}

完整的 Contract:

public interface MainContract {
    interface Model {
        Flowable> login(String username, String password);
    }

    interface View extends BaseView {
        @Override
        void showLoading();

        @Override
        void hideLoading();

        @Override
        void onError(Throwable throwable);

        void onSuccess(BaseObjectBean bean);
    }

    interface Presenter {
        /**
         * 登陸
         *
         * @param username
         * @param password
         */

        void login(String username, String password);
    }
}

在 MainContract 中

  • Model介面: 建立對應的聯網請求的方法,將 Presenter 提交的欄位放到聯網請求中,傳送給伺服器

  • View介面: 建立在介面上顯示載入中、取消載入以及登陸成功、失敗的方法

  • Presenter介面: 建立 登陸的方法,以及需要提交的欄位 (username、password)

MainModel的完整程式碼:

public class MainModel  implements MainContract.Model {
    @Override
    public Flowable> login(String username, String password) {
        return RetrofitClient.getInstance().getApi().login(username,password);
    }
}

Model 類實現 MainContract.Model 介面中的 login(String username, String password)方法,將 username、password 放在聯網請求中,進行請求伺服器。

MainView 的完整程式碼:

public class MainActivity extends BaseMvpActivity<MainPresenterimplements MainContract.View {

    @BindView(R.id.et_username_login)
    TextInputEditText etUsernameLogin;
    @BindView(R.id.et_password_login)
    TextInputEditText etPasswordLogin;

    @Override
    public int getLayoutId() {
        return R.layout.activity_main;
    }

    @Override
    public void initView() {
        mPresenter = new MainPresenter();
        mPresenter.attachView(this);
    }

    /**
     * @return 帳號
     */

    private String getUsername() {
        return etUsernameLogin.getText().toString().trim();
    }

    /**
     * @return 密碼
     */

    private String getPassword() {
        return etPasswordLogin.getText().toString().trim();
    }

    @Override
    public void onSuccess(BaseObjectBean bean) {

        Toast.makeText(this, bean.getErrorMsg(), Toast.LENGTH_SHORT).show();

    }

    @Override
    public void showLoading() {
        ProgressDialog.getInstance().show(this);
    }

    @Override
    public void hideLoading() {
        ProgressDialog.getInstance().dismiss();
    }

    @Override
    public void onError(Throwable throwable) {

    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // TODO: add setContentView(...) invocation
        ButterKnife.bind(this);
    }

    @OnClick(R.id.btn_signin_login)
    public void onViewClicked() {
        if (getUsername().isEmpty() || getPassword().isEmpty()) {
            Toast.makeText(this"帳號密碼不能為空", Toast.LENGTH_SHORT).show();
            return;
        }
        mPresenter.login(getUsername(), getPassword());
    }
}

MainActivity 中實現 MainContract.View中的方法 ,在實現的方法中,進行進度條載入、和登陸成功or失敗的UI的展示:

        @Override
        void showLoading();

        @Override
        void hideLoading();

        @Override
        void onError(Throwable throwable);

        void onSuccess(BaseObjectBean bean);

MainPresenter 的完整程式碼:

public class MainPresenter extends BasePresenter<MainContract.Viewimplements MainContract.Presenter {

    private MainContract.Model model;

    public MainPresenter() {
        model = new MainModel();
    }

    @Override
    public void login(String username, String password) {
        if (!isViewAttached()) {
            return;
        }
        mView.showLoading();
        model.login(username, password)
                .compose(RxScheduler.>Flo_io_main())
                .as(mView.>bindAutoDispose())
                .subscribe(new Consumer>() {
                    @Override
                    public void accept(BaseObjectBean bean) throws Exception {
                        mView.onSuccess(bean);
                        mView.hideLoading();
                    }
                }, new Consumer() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        mView.onError(throwable);
                        mView.hideLoading();
                    }
                });
    }
}

MainPresenter 實現 MainContract.Presenter 介面中的 login(String username, String password) 方法

實體化Model,在 MainPresenter login(String username, String password)方法中,呼叫 model的網路請求,將 username、password 放在model 的 login() 方法中,進行請求伺服器。
請求伺服器前 使用 MainContract.View 中的 mView.showLoading()方法,進行顯示載入中;在成功失敗的回呼中,使用對應的方法,以及取消載入。

其中BasePresenter、BaseView 是對Presenter以及View進行的封裝

BaseView類:

public interface BaseView {

    /**
     * 顯示載入中
     */

    void showLoading();

    /**
     * 隱藏載入
     */

    void hideLoading();

    /**
     * 資料獲取失敗
     * @param throwable
     */

    void onError(Throwable throwable);

    /**
     * 系結Android生命週期 防止RxJava記憶體洩漏
     *
     * @param 
     * @return
     */

     AutoDisposeConverter bindAutoDispose();

}

至於為什麼不把 onSuccess()方法也封裝,是因為請求網路,伺服器傳回的值是不一樣的,在 Contract > View 介面中根據 bean 類設定 onSuccess()

BasePresenter類:

public class BasePresenter<V extends BaseView{
    protected V mView;


    /**
     * 系結view,一般在初始化中呼叫該方法
     *
     * @param view view
     */

    public void attachView(V view) {
        this.mView = view;
    }

    /**
     * 解除系結view,一般在onDestroy中呼叫
     */


    public void detachView() {
        this.mView = null;
    }

    /**
     * View是否系結
     *
     * @return
     */

    public boolean isViewAttached() {
        return mView != null;
    }
}

時間有限,暫時就先這樣,具體可下載 Demo檢視 ↓

本Demo:https://github.com/RookieExaminer/MvpDemo
MVP快速生成類的外掛:
https://github.com/githubwing/MVPHelper

參考:

Android MVP架構搭建:http://www.jcodecraeer.com/a/anzhuokaifa/2017/1020/8625.html?1508484926

Android架構中新增AutoDispose解決RxJava記憶體洩漏:https://www.jianshu.com/p/8490d9383ba5



●編號360,輸入編號直達本文

●輸入m獲取到文章目錄

推薦↓↓↓

Java程式設計

更多推薦18個技術類公眾微信

涵蓋:程式人生、演演算法與資料結構、駭客技術與網路安全、大資料技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、資料庫、運維等。

贊(0)

分享創造快樂