题记
本系列课程简单的介绍了MVC在软件开发中的运用,以及实际运用时该模式存在的一些问题。引出更适合当下情况的MVP与具体的实现过程。在基础MVP框架之上,介绍了Dagger2,Retrofit2,RxJava在MVP模式下的引入理由以及使用。
为什么进行重构?
- 便于单元测试的引入(可测试)
- 提高代码可读性
- 业务逻辑与UI分离
MVC
View:视图,泛指我们能够看到的页面。用户可以发起操作在Controller层进行处理,也可以直接对数据层进行处理。
Controller:控制器,处理用户交互逻辑,更新View与操作数据。
Model:数据层,JavaBean实体类,用于保存实例数据。
优点:层次区分清晰,各部分可直接调用,减少了代码量。
缺点:在android开发的实际运用中,往往是Activity即作为View又作为Controller,需要加载应用的布局和初始化用户界面,接受并处理来自用户的操作请求,以及请求数据并对界面进行刷新。当业务逻辑或者交互上略复杂,会导致整个类的臃肿,进而难易维护。
业务场景
我们用一个非常简单的填写账号密码点击按钮注册的例子。
布局如图所示
要求:
- 判断账号密码不能为空。
- 密码保证6-18位。
- 发起网络请求回调判断是否注册成功。
我们来看用MVC方式写的基本代码
private EditText edtAccount;
private EditText edtPassword;
private Button btnRegister;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
initView();
initEvent();
}
private void initView() {
edtAccount = (EditText) findViewById(R.id.edt_account);
edtPassword = (EditText) findViewById(R.id.edt_password);
btnRegister = (Button) findViewById(R.id.btn_login);
}
private void initEvent() {
btnRegister.setOnClickListener(onClickListener);
}
View.OnClickListener onClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
doRegister();
}
};
private void doRegister() {
if (checkInput()) {
if (doHttp()) {
showSuccess();
} else {
Toast.makeText(this, "注册失败", Toast.LENGTH_SHORT);
}
}
}
private boolean doHttp() {
return true;
}
private boolean checkInput() {
if (TextUtils.isEmpty(edtAccount.getText().toString())) {
showErrorMsg("帐号不能为空");
return false;
}
if (TextUtils.isEmpty(edtPassword.getText().toString())) {
showErrorMsg("密码不能为空");
return false;
}
return checkPassword();
}
private boolean checkPassword() {
String password = edtPassword.getText().toString();
if (password.length() >= 6 && password.length() <= 18)
return true;
showErrorMsg("请确认密码为6-18位");
return false;
}
private void showErrorMsg(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT);
}
private void showSuccess() {
Toast.makeText(this, "注册成功", Toast.LENGTH_SHORT);
}
在不包括网络请求的情况下,上诉代码不包括头文件已经达到了80+行,boom!!!。
MVP
图片来自wiki
View:Activity,fragment,View等在Android环境下特有的包下的内容。
presenter:业务逻辑处理层,驱动View层更新。区别于View层,包含JAVA层的内容,用于单元测试。
Model:数据层,包括网络请求,数据库操作等,将数据回调到P层。
定义一个契约类来对MVP之间的交互接口进行管理,接下来就围绕contract进行展开。
先分析下各模块所要处理的事:
View:
- 视图绑定
- 监听绑定
- 错误吐司
- 成功吐司
Presenter:
- 验证账号密码是否为空
- 验证密码长度是否符合
- 进行网络请求,根据回调进行吐司
参与交互的方法只有View的Toast方法与Presenter提供的doRegister,于是contract就变成了这个样子
public interface RegisterContract {
interface View {
void showSuccessToast();
void showErrorMsg(String msg);
}
interface Presenter {
void doRegister(String account, String password);
}
}
准备工作完毕,回到Activity中。
public class MVPMainActivity extends AppCompatActivity implements RegisterContract.View {
private EditText edtAccount;
private EditText edtPassword;
private Button btnRegister;
private RegisterPresenter mPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
//绑定视图
initView();
//绑定点击监听事件
initEvent();
//初始化presenter
initPresenter();
}
private void initView() {
edtAccount = (EditText) findViewById(R.id.edt_account);
edtPassword = (EditText) findViewById(R.id.edt_password);
btnRegister = (Button) findViewById(R.id.btn_login);
}
private void initEvent() {
btnRegister.setOnClickListener(onClickListener);
}
private void initPresenter() {
mPresenter = new RegisterPresenter(this);
}
View.OnClickListener onClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
mPresenter.doRegister(edtAccount.getText().toString()
, edtPassword.getText().toString());
}
};
@Override
public void showSuccessToast() {
Toast.makeText(this, "注册成功", Toast.LENGTH_SHORT);
}
@Override
public void showErrorMsg(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT);
}
}
Presenter实现:
public class RegisterPresenter implements RegisterContract.Presenter {
RegisterContract.View mView;
public RegisterPresenter(RegisterContract.View view) {
mView = view;
}
@Override
public void doRegister(String account, String password) {
if (checkInput(account, password)) {
if (doHttp(account, password)) {
mView.showSuccessToast();
} else {
mView.showErrorMsg("注册失败");
}
}
}
private boolean doHttp(String account, String password) {
return true;
}
private boolean checkInput(String account, String password) {
if (TextUtils.isEmpty(account)) {
mView.showErrorMsg("帐号不能为空");
return false;
}
if (TextUtils.isEmpty(password)) {
mView.showErrorMsg("密码不能为空");
return false;
}
return checkPassword(password);
}
private boolean checkPassword(String password) {
if (password.length() >= 6 && password.length() <= 18)
return true;
mView.showErrorMsg("请确认密码为6-18位");
return false;
}
}
至此,我们已经使用MVP模式实现了除网络部分的所有代码。
总结
MVP的优势
- presenter中所有的代码都是纯JAVA,即不包含Android下的内容,便于单元测试工作的开展;
- 代码可读性得到提高;
- 两者最主要的区别在于MVP不能进行View和Model的直接交互,从而抽离业务逻辑,使Activity或者Fragment变的不是那么的臃肿。
MVP的缺点
类爆炸。解耦与清晰的代价就是产生过多的类,所以在实现较为简单的页面时,仍可采用MVC的模式进行实现。
demo
参考
Android MVP模式 简单易懂的介绍方式