聊聊Android开发中的MVP模式

时间:2020-12-18 21:14:41

原文链接:http://silencezwm.github.io/android/2016/06/04/聊聊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,旨在引导我等开发者,并非强制我们必须按照他的模式来哦,有兴趣的可以去研究研究。