聊聊Android开发中的MVP模式

作者: silencezwm | 来源:发表于2016-07-19 23:29 被阅读741次

    原文链接:
    https://juejin.im/post/5a055a826fb9a045055d974d

    一、初识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,旨在引导我等开发者,并非强制我们必须按照他的模式来哦,有兴趣的可以去研究研究。

    相关文章

      网友评论

      • 倔強Ykuk:也就是看看,我认为一般的项目不需要用这样的框架,1.代码极多,2.可能会出现多层内部类嵌套代码可读性差,如:在新线程网络操作时,直接调用bussiness的onSuccess,进而调用view的onSuccess在view的onSuccess中可能进行了view操作。为了解决这样的问题,不得不在bussiness中多嵌套一个回调函数才能使得onSuccess在主线程被调用。所以我还是建议如果说你的页面逻辑并不很复杂,完全可以用正常的方式来做,开发速度快,代码维护也不是那么麻烦,而且改问题时没必要因为一个地方改动牵动其他的类。
      • KunMinX:传统的mvp写法个人觉得有些问题,例如presenter不像是与view平级而更像是挂接在view之下的。建议参考下谷歌的mvp,它的契约类个人觉得很棒。我将写一篇文章专门讲谷歌mvp的好处。
      • ZeiHa:没看懂

      本文标题:聊聊Android开发中的MVP模式

      本文链接:https://www.haomeiwen.com/subject/rlcvjttx.html