聊聊Android開發中的MVP模式

silencezwm發表於2017-11-12


一、初識MVP(Model View Presenter)

google上關於MVP模式的資料已經特別多了,所以我這裡也不囉嗦了。 因為之前做過的幾個專案,每個Activity的所有操作程式碼全部都是堆在裡面,雖然查詢還算方便,但是程式碼動不動就上千行,所以維護起來特別麻煩,如果程式碼沒有註釋,那對於我們來說簡直就是災難! 所以這次決定把MVP模式放在真實專案中玩玩,以下也是我從真實專案中使用的一些小感受。

二、專案例項----【登入+註冊+獲取驗證碼+重置密碼】

2.1 MVP中的Model 首先我們得建立一個存放Model的包,我這裡命名為Biz。 AccountListener類中存放的都是相關操作的介面,每個介面中都有操作成功和操作失敗的方法回撥,至於裡面的引數都可根據實際情況進行定義。

/**
  * author:silencezwm on 16/6/1 10:24
  * email:silencezwm@gmail.com
  * description:賬號監聽
  */
public class AccountListener {

    /**
      * 登入監聽介面
      */
    public interface LoginListener {
        void loginSuccess();
        void loginFailed(String msg);
    }

    /**
      * 註冊賬號監聽介面
      */
    public interface RegListener {
        void regSuccess();
        void regFailed(String msg);
    }

    /**
      * 獲取驗證碼監聽介面
      */
    public interface GetVerCodeListener {
        void getVerCodeSuccess();
        void getVerCodeFailed(String msg);
    }

    /**
      * 獲取忘記密碼驗證碼監聽介面
      */
     public interface GetForgetPwdVerCodeListener {
         void getForgetPwdVerCodeSuccess();
         void getForgetPwdVerCodeFailed(String msg);
     }

    /**
      * 核驗驗證碼是否正確
      */
    public interface CheckVerCodeListener{
        void checkVerCodeSuccess();
        void checkVerCodeFailed(String msg);
    }

    /**
      * 重置密碼監聽介面
      */
    public interface ResetPwdListener {
        void resetPwdSuccess();
        void resetPwdFailed(String msg);
    }
}
複製程式碼

在IAccountBiz介面中定義的都是請求網路需要的引數,同樣也放在Model下。

/**
  * author:silencezwm on 16/6/1 11:33
  * email:silencezwm@gmail.com
  * description:賬戶相關操作所有介面Biz
  */
public interface IAccountBiz {

    /**
      *
      * @param userName          使用者名稱
      * @param pwd               密碼
      * @param loginListener     使用者登入監聽
      */
    void login(String userName, String pwd, LoginListener loginListener);

    /**
      *
      * @param mobile        手機號
      * @param password      密碼
      * @param validateCode  驗證碼
      * @param regListener   使用者註冊監聽
      */
    void register(String mobile, String password, String validateCode, RegListener regListener);

    /**
      *
      * @param mobile                手機號
      * @param getVerCodeListener    獲取驗證碼監聽
      */
    void getVerifyCode(String mobile, GetVerCodeListener getVerCodeListener);

    /**
      *
      * @param mobile                            手機號
      * @param getForgetPwdVerCodeListener       獲取忘記密碼驗證碼監聽
      */
    void getForgetPwdVerifyCode(String mobile,GetForgetPwdVerCodeListener getForgetPwdVerCodeListener);

    /**
      *
      * @param mobile                手機號
      * @param verCode               驗證碼
      * @param checkVerCodeListener  核驗驗證碼是否正確監聽
      */
    void checkVerCode(String mobile, String verCode, AccountListener.CheckVerCodeListener checkVerCodeListener);

    /**
      *
      * @param mobile            手機號
      * @param newPwd            新密碼
      * @param resetPwdListener  重置密碼監聽
      */
    void resetPwd(String mobile, String newPwd, ResetPwdListener resetPwdListener);
    }
複製程式碼

Model下的AccountBiz類實現IAccountBiz介面,並顯示其所有方法,這裡就會進行網路請求,這裡我會隱藏部分網路請求程式碼。

    /**
      * author:silencezwm on 16/6/1 12:00
      * email:silencezwm@gmail.com
      * description:登入邏輯處理
      */
    public class AccountBiz implements IAccountBiz {

        /**
          *
          * @param userName          使用者名稱
          * @param pwd               密碼
          * @param loginListener     使用者登入監聽
          */
        @Override
        public void login(String userName, String pwd, final LoginListener loginListener) {

            *******這裡編寫網路請求程式碼*******
            -------請求成功後,呼叫loginListener.loginSuccess();
            -------請求失敗後,呼叫
          loginListener.loginFailed(msg);   

          *******其他幾個請求方法同上,此處省略*******
        }
複製程式碼

**到這裡Model層的工作基本完成了,接下來我們瞧瞧View層。

2.2 MVP中的View View層完全不用去管Model層做了啥,怎麼做的問題,它只需要把Activity伺候好就行。 AccountView類中放在View包下,所有在Model中需要Activity提供的資料或者Model層和伺服器互動後返回的資料也由View傳給Activity的。

    /**
      * author:silencezwm on 16/6/1 12:25
      * email:silencezwm@gmail.com
      * description:登入相關
      */
    public class AccountView {

        /**
          * 登入介面
          */
        public interface ILoginView {

              /**
              * @return 獲取使用者名稱
              */
              String getUserName();

              /**
               * @return 獲取密碼
               */
              String getPwd();

              /**
                * 登入成功後返回資訊
                */
              void loginSuccess();

              /**
                * 登入失敗
                */
              void loginFailed(String msg);

        }

        /**
          * 獲取驗證碼介面
          */
        public interface IGetVerCodeView {

            /**
              * @return 獲取手機號
              */
            String getMobile();

            /**
              * 獲取驗證碼成功
              */
            void getVerCodeSuccess();

            /**
              * 獲取驗證碼失敗
              */
            void getVerCodeFailed(String msg);
        }

        /**
          * 獲取忘記密碼驗證碼介面
          */
        public interface IGetForgetPwdVerCodeView {

            /**
              * @return 獲取手機號
              */
            String getMobile();

            /**
              * 獲取忘記密碼驗證碼成功
              */
            void getForgetPwdVerCodeSuccess();

            /**
              * 獲取忘記密碼驗證碼失敗
              */
            void getForgetPwdVerCodeFailed(String msg);
        }

        /**
          * 核驗驗證碼是否正確介面
          */
        public interface ICheckVerCodeView {

            /**
              * @return 獲取手機號
              */
            String getCheckVerCodeMobile();

            /**
              *
              * @return  驗證碼
              */
            String getCheckVerCode();

            /**
              * 核驗驗證碼成功
              */
            void checkVerCodeSuccess();

            /**
              * 核驗驗證碼失敗
              */
            void checkVerCodeFailed(String msg);
        }

        /**
          * 註冊介面
          */
        public interface IRegView {
            /**
              * @return 獲取手機號
              */
            String getRegMobile();

            /**
              * @return 獲取驗證碼
              */
            String getRegVerCode();

            /**
              * @return 獲取密碼
              */
            String getRegPwd();

            /**
              * 註冊成功
              */
            void regSuccess();

            /**
              * 註冊失敗
              */
            void regFailed(String msg);
        }

        /**
          * 重置密碼
          */
        public interface IResetPwdView {
            /**
              * @return 新密碼
              */
            String getNewPwd();

            /**
              * @return 手機號
              */
            String getMobile();

            /**
              * 重置密碼成功
              */
            void resetPwdSuccess();

            /**
              * 重置密碼失敗
              */
            void resetPwdFailed(String msg);
        }
    }
複製程式碼

到這裡View層的工作也完成了,其實難度並不大,後續相關Activity只需實現View中的相關介面,並顯示其中所有的方法即可

2.3 MVP中的的主角Presenter 先打個比方,牛郎和織女不是每年得相會嘛,他們中間得有座橋才行。在MVP中Model和View就像牛郎織女,而橋就是Presenter,所以正是Presenter把Model和View聯絡在一起的,這樣也使得程式碼得到了解耦,各層各司其職。

    /**
      * author:silencezwm on 16/6/1 12:55
      * email:silencezwm@gmail.com
      * description:登入Presenter
      */
    public class LoginPresenter {

        private IAccountBiz mILoginBiz;
        private ILoginView mILoginView;

        //在相關Activity中例項化此Presenter,並傳入相關View
        public LoginPresenter(ILoginView ILoginView) {
            mILoginView = ILoginView;
            mILoginBiz = new AccountBiz();
        }

        public void login(){mILoginBiz.login(mILoginView.getUserName(), mILoginView.getPwd(), new LoginListener() {
                @Override
                public void loginSuccess() {
                        //登入成功後,呼叫View的回撥方法,將成功資訊返回給Activity
                    mILoginView.loginSuccess();
                }

                @Override
                public void loginFailed(String msg) {
                    //登入失敗後,呼叫View的回撥方法,將錯誤資訊返回給Activity
                    mILoginView.loginFailed(msg);
                }


            });
        }
    }
複製程式碼

此時這根橋就搭建完畢了,搞了這麼久,現在我們終於可以在相關Activity中進行使用了

2.4 在相關Activity中使用 1、實現相應View介面,並實現其所有方法 2、例項化Presenter,並去呼叫相關方法 接下來看具體程式碼實現:

/**
  * author:silencezwm on 16/6/1 13:16
  * email:silencezwm@gmail.com
  * description:登入Fragment
  */
public class LoginFragment extends BaseFragment     implements ILoginView {

@Bind(R.id.et_input_username)
EditText et_input_username;
@Bind(R.id.et_input_pwd)
EditText et_input_pwd;

private View loginView;

//例項化登入相關Presenter
private LoginPresenter mLoginPresenter = new LoginPresenter(this);


        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            loginView = inflater.inflate(R.layout.fragment_login, container, false);
            ButterKnife.bind(this, loginView);

            return loginView;
        }

        @OnClick({R.id.btn_login, R.id.text_forget_pwd})
        public void clickOpreation(View v) {
            switch (v.getId()) {
                //登入
                case R.id.btn_login:

                    if (et_input_username.getText().toString().isEmpty()){
                        ToastUtil.showToast(getActivity(), "手機號不能為空");
                        return;
                    }
                    if (et_input_pwd.getText().toString().isEmpty()){
                        ToastUtil.showToast(getActivity(), "密碼不能為空");
                        return;
                    }
                    if(!RegexUtils.checkMobile(et_input_username.getText().toString())){
                        ToastUtil.showToast(getActivity(), "請輸入合法的手機號");
                        return;
                    }

                    //登入
                    mLoginPresenter.login();

                    break;

                //忘記密碼
                case R.id.text_forget_pwd:
                    openActivity(ForgetPwdActivity.class);
                    break;
            }

        }


        /**
          * 獲取輸入使用者名稱
          *
          * @return
          */
         @Override
        public String getUserName() {
            return et_input_username.getText().toString();
        }

        /**
          * 獲取輸入密碼
          *
          * @return
          */
        @Override
        public String getPwd() {
            return et_input_pwd.getText().toString();
        }

        /**
          * 登入成功
          */
         @Override
        public void loginSuccess() {
            ToastUtil.showToast(getActivity(), "登入成功");
        }

        /**
          *
          * @param msg   登入失敗資訊
          */
        @Override
        public void loginFailed(String msg) {
            ToastUtil.showToast(getActivity(), msg);
        }

        @Override
        public void onDestroy() {
            super.onDestroy();
            ButterKnife.unbind(this);
        }

}
複製程式碼

三、MVP小總結 專案中使用了MVP的感受就是:

1、使用MVP後,程式碼量稍微多了點
2、把現在Activity和之前Activity相比較,使用MVP後,Activity中的程式碼量大幅度下降,程式碼閱讀、維護更方便。
複製程式碼

另外google官方也放出了一個MVP模式的專案android-architecture,旨在引導我等開發者,並非強制我們必須按照他的模式來哦,有興趣的可以去研究研究。



相關文章