相信大家看这篇文章的时候,应该都是积累了一定的Android实际项目开发经验的,大家一定都是这么经历过来的:所有的业务逻辑实现以及一些界面相关(Dialog,PopWindow....显示)还有网络请求的CallBack都放在Activity里面,从界面的初始化,数据加载,然后根据网络返回显示界面的不同内容,少则一个activity几百行代码,多则上千。我想如果稍微出点bug或者说需求做一些稍微的改动,大家就会烦恼,因为Activity所涉及的业务太多,逻辑太繁杂。可能找一个bug又得从头看一遍代码。下面介绍一下MVP设计模式,从此解耦界面与业务逻辑,轻轻松松对付产品,对付bug.
MVP设计模式简介:
其实说起MVP设计模式,应该要先去了解一下mvc设计模式,android本身就是一种MVC的设计模式:网络,数据库,本地文件的数据存取作为Model,Acitivity作为Controller,而xml文件还有一些界面相关的组件作为View。但是这样的设计在我们真正去做项目的时候可能完美的体现出来么?这是不可能的,往往我们要在Activity里面写很多界面的东西,比如Dialog,PopWindow等等,也就是说,View层和Controller层紧紧联系在一起,造成代码的高度耦合。而且在MVC里,是允许View层直接访问Model层的。而mvp的出现就是用Presenter代替以前的Controller,实现V,C层的完美分离,当然这个实现需要接口的辅助。看完下面的例子就明白了。
MVP完美实现:
1.定义View接口:
public interface ILoginView extends IBaseView {
void showNetWorkError(); //显示网络错误
void showLogining();//显示登录中
void showLoginSuccess();//显示登录成功
void showLoginFail(String msg);//显示登录失败,并说明服务器登录错误的原因
}
IBaseView其实就是一个空的interface,这个具体根据你的项目而定,看看界面需要定义的共同方法是什么,可以加到父接口中,一般用空接口就行了。
2.定义Presenter
public class LoginPresenter extends MvpBasePresenter<ILoginView> {
public void login(Context context, String username, String password) {
if (isViewAttached()) {//判断界面是否还在(用户没有点返回键)
if (Utils.hasNetWork(context)) { //无网络,显示网络无连接
getView().showNetWorkError();
return;
}
getView().showLogining(); //显示正在登录中
DataModel.login(context, username, password, new NetWorkCallBack() { //访问Model
@Override
public void onSuccess(String res) {
if (isViewAttached()) {
getView().showLoginSuccess(); //显示登录成功
}
}
@Override
public void onFail(String failMsg) {
if (isViewAttached()) {
getView().showLoginFail(failMsg);//显示登录失败
}
}
});
}
}
}
在看一看MvpBasePresenter(可以作为模板代码直接使用)
public class MvpBasePresenter<V extends MvpView> implements MvpPresenter<V> {
private WeakReference<V> viewRef;
/**
* 界面创建,Presenter与界面取得联系
*/
@Override
public void attachView(V view) {
viewRef = new WeakReference<V>(view);
}
/**
* 获取界面的引用
*/
protected V getView() {
return viewRef == null ? null : viewRef.get();
}
/**
* 判断界面是否销毁
*/
protected boolean isViewAttached() {
return viewRef != null && viewRef.get() != null;
}
/**
* 界面销毁,Presenter与界面断开联系
*/
@Override
public void detachView(boolean retainInstance) {
if (viewRef != null) {
viewRef.clear();
viewRef = null;
}
}
}
3,最后定义Activity实现View接口
public class LoginActivity extends MvpActivity<ILoginView, LoginPresenter> implements ILoginView {
private EditText etUserName, etPassword;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn_login).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
presenter.login(LoginActivity.this, etUserName.getText().toString(), etPassword.getText().toString());
}
});
}
@Override
protected LoginPresenter createPresenter() {
return new LoginPresenter(); //具体界面对应的presenter由具体界面决定
}
@Override
public void showNetWorkError() {
Toast.makeText(this, "网络有问题,请检查网路连接", Toast.LENGTH_SHORT).show();
}
@Override
public void showLogining() {
Toast.makeText(this, "正在登录中", Toast.LENGTH_SHORT).show();
}
@Override
public void showLoginSuccess() {
Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show();
}
@Override
public void showLoginFail(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
}
看看MvpActivity(可以作为模板代码使用)
public abstract class MvpActivity<V extends MvpView, P extends MvpPresenter> extends Activity implements MvpView {
protected P presenter;
@SuppressWarnings("unchecked")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
presenter = createPresenter();
presenter.attachView(this); //presenter取得与界面的联系
}
@Override
protected void onDestroy() {
super.onDestroy();
presenter.detachView(false);//presenter断开与界面的联系
}
protected abstract P createPresenter();
}
好了整个MVP的模板写完了,大家回头看一看,如果这个时候产品跟你说,我这些提示不想要Toast来提示,你改成对话框的形式,那简单啊,直接改Activity那些show方法的实现就可以了,根本不需要再查看一遍逻辑,V C分离就是好,改起来超快。
仅仅只有这一个优点么?
No,往往我们在开发产品的时候,UI和接口很难达到同步,这个时候MVP就派上用场了,如果界面出了,接口没出,我们是不是可以先把Activity和Presenter给实现了呢?等到接口出来,我们直接写Model层的代码就ok了,再也不需要傻傻的等着了。
有人说mvp会导致接口泛滥,但是你用了之后,不觉得多写两个接口就可以方便这么多,而且维护方便了不少么?