聊聊Android开发中的MVP模式



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



最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容