现在的安卓开发已经很成熟,不像几年前大部分人还处于技术摸索阶段,当技术问题不再是安卓开发的难题时,更多人开始关注架构设计,代码质量,更想易于测试,维护方便,逻辑清晰。大家试图让所有的代码都高度解耦,各层分离,从而达到目的。MVP的架构模式,就这样应运而生。这里我希望以最简单的例子,让大家快速入门MVP模式。
MVP就是Model,View,Presenter的缩写。
对比MVC,MVP模式的特点是V层和M层不可互相接触,需要通过主导器Presenter进行关联,P层进行所有的逻辑处理,V层仅仅负责视图展示,M层负责获取数据。
下面是MVC和MVP对比图:
图片出处
依然是最简单的登录页面,我通过一个登录业务,来详述MVP在项目中的使用,文后会附上代码地址。
既然要解耦,必然是面向接口的编程,就按照写代码的顺序从Model开始看:
LoginModel.java
/** * Created by yuankundong on 2016/03/03. */
public interface LoginModel {
void toLogin(String username, String password, OnLoginListener onLoginListener);
}
对于Model层,没有什么多余的方法,就是完成登录,把结果返回给Presenter。这里我又写了一个接口OnLoginListener,为了给Presenter回调,让View和Model分离,当然,如果我们的框架中使用了otto之类的东东,那就不需要这个接口。接着看上面接口的实现:
LoginModelImpl.java
/** * Created by yuankundong on 2016/03/03. */
public class LoginModelImpl implements LoginModel{
String url = "http://xxxxx";
public final static int TIME_OUT_CODE = 99;
public final static String TIME_OUT_MSG = "网络连接不可用,请稍后重试";
@Override
public void toLogin(final String username, final String password, final OnLoginListener onLoginListener) {
StringRequest stringRequest = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Type type = new TypeToken<UserBean>() {
}.getType();
Gson gson = new Gson();
UserBean userBean = gson.fromJson(response, type);
if (userBean.isSuccess()) {
onLoginListener.OnSuccess(userBean);
} else {
onLoginListener.OnError(userBean.getCode(), userBean.getMsg());
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
onLoginListener.OnError(TIME_OUT_CODE, TIME_OUT_MSG);
}
}) {
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> map = new HashMap<>();
map.put("phone", username);
map.put("password", MD5Utils.stringToMD5(password));
return map;
}
};
BaseApplication.requestQueue.add(stringRequest);
}
}
这里使用Volley网络框架,假设登录成功后,返回的json数据是这个类型:
{
"code": 0,
"msg": "登录成功" }
那么我的实体Bean可以这么设计:
/** * Created by yuankundong on 2016/03/03. */
public class UserBean {
private int code; // 返回码,0为成功
private String msg; // 返回信息
public UserBean(int code, String msg) {
this.code = code;
this.msg = msg;
}
public boolean isSuccess() {
return code == 0;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
上述代码就完成了Model层,因为Presenter是M和V的联络人,所以,我们再把View层写了,同样,需要先写接口,这个接口里面我们写什么呢,就是在页面上,会发生的所有事,比如,获取用户名密码,点击登录后有进度条提示,登录反馈后取消提示等。
/** * Created by yuankundong on 2016/03/03. */
public interface LoginView {
//取得用户名
String getUsername();
//取得密码
String getPassword();
//登录成功后
void loginSuccess();
//登录失败后
void loginError(String msg);
//展示进度条
void showProgress();
//隐藏进度条
void hideProgress();
//当需要弹出提示信息时
void showMessage(String msg);
}
我们能想到的都写进来了,接着我们写这个接口的实现类,就是个Activity。
/** * Created by yuankundong on 2016/03/03. */
public class LoginActivity extends AppCompatActivity implements LoginView{
TextView username;
TextView password;
Button login;
ProgressDialog dialog;
LoginPresenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
presenter = new LoginPresenterImpl(this);
username = (TextView) findViewById(R.id.tv_username);
password = (TextView) findViewById(R.id.tv_password);
login = (Button) findViewById(R.id.btn_login);
login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
presenter.actionLogin();
}
});
}
@Override
public String getUsername() {
return username.getText().toString();
}
@Override
public String getPassword() {
return password.getText().toString();
}
@Override
public void loginSuccess() {
Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show();
}
@Override
public void loginError(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
@Override
public void showProgress() {
dialog = ProgressDialog.show(this, " ", "加载中", true, true);
}
@Override
public void hideProgress() {
dialog.dismiss();
}
@Override
public void showMessage(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
}
看上面的代码,很简洁,没有任何逻辑代码,这就是我们想要的效果,接下来就开始写Presenter层了,逻辑代码也是写到这一层。
/** * Created by yuankundong on 2016/03/03. */
public interface LoginPresenter {
void actionLogin();
}
实现:
/** * Created by yuankundong on 2016/03/03. */
public class LoginPresenterImpl implements LoginPresenter, OnLoginListener {
LoginView loginView;
LoginModel loginModel;
public LoginPresenterImpl(LoginView loginView) {
this.loginView = loginView;
this.loginModel = new LoginModelImpl();
}
@Override
public void actionLogin() {
String username = loginView.getUsername();
String password = loginView.getPassword();
if (TextUtils.isEmpty(username)) {
loginView.showMessage("用户名为空");
return;
}
if (TextUtils.isEmpty(password)) {
loginView.showMessage("密码为空");
return;
}
loginView.showProgress();
loginModel.toLogin(username,password,this);
}
@Override
public void OnSuccess(UserBean userBean) {
loginView.hideProgress();
loginView.loginSuccess();
}
@Override
public void OnError(int code, String msg) {
loginView.hideProgress();
loginView.loginError(msg);
}
}
在LoginPresenterImpl 类中,我们实例了LoginView 和LoginModel 。登录的逻辑就是先判断用户名和密码是否为空,不为空再调用Model里的登录方法,进行登录。因为Model层把登录反馈传给OnLoginListener 这个回调接口,所有我们在这里实现这个接口,就能获取登录后的反馈。OnSuccess和OnError分别是登录成功和失败后,通知View层应该显示什么。
这样,我们用MVP写出来的登录就完成了,总体思想就是,Model层获取的数据交给Presenter处理,Presenter进行处理的过程中,会通知页面View层进行相应变化,View层本身不进行逻辑处理,MVP架构及时各有分工。
整体工程里面还有个别不涉及本文内容的我没有详述,源码地址,点击这里下载