- 概念解释
- MVP是Model(数据) View(界面) Presenter(表现层)的缩写,它是MVC架构的变种,强调Model和View的最大化解耦和单一职责原则;
- Model:负责数据相关的业务逻辑,包括数据库,网络,本地缓存,内存数据的业务逻辑处理,并提供接口暴露自己处理数据的状态和进度。
- View:负责UI相关的业务逻辑,包括显示对话框,Toast,展示listview,gridview等所有的UI相关逻辑,并提供接口暴露自己处理UI逻辑的状态和进度。
- Presenter:Model和View的互相调用的桥梁,本身不应该有任何具体的逻辑代码,只是在对应的状态下调用Model和View的方法实现数据相关逻辑和UI展示相关逻辑,并且与Fragment和Activity直接打交道;
- 目前MVP的P有分歧,部分人当Presenter独立抽出,与Activity或Fragment交互,还有部分人直接将Activity和Fragment充当Presenter
- 先看看一个登录界面的数据业务逻辑和UI逻辑全部写在Activity的代码:
public class LoginActivity extends ActionBarActivity implements OnClickListener{
EditText etUsername,etPassword;
Button btn_login;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
etUsername = (EditText) findViewById(R.id.et_username);
etPassword = (EditText) findViewById(R.id.et_password);
btn_login = (Button) findViewById(R.id.btn_login);
//设置点击事件
btn_login.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_login:
String username = etUsername.getText().toString();
String password = etPassword.getText().toString();
if(checkInput(username,password)){
//提交登录
//显示正在登录对话框
showLoginDialog(this);
//执行登录请求
execLogin(username, password);
}
break;
}
}
private ProgressDialog progressDialog;
//显示登录进度对话框
public void showLoginDialog(Context context){
if(progressDialog==null){
progressDialog = new ProgressDialog(context);
progressDialog.setTitle("正在登录中...");
}
progressDialog.show();
}
//关闭登录进度对话框
public void hideLoginDialog(Context context){
if(progressDialog!=null){
progressDialog.dismiss();
}
}
//执行登录请求
public void execLogin(String username,String password){
//执行登录请求的伪代码
HttpHelper helper = new HttpHelper();
helper.execRequest("http://www.baidu.com", new HttpCallback() {
@Override
public void onSuccess() {
//关闭登录对话框
hideLoginDialog(LoginActivity.this);
//提示登录成功
showLoginSuccess(LoginActivity.this);
//保存登录相关数据,如登录的标记,用户的唯一标识
saveLoginData();
}
@Override
public void onFail() {
//关闭登录对话框
hideLoginDialog(LoginActivity.this);
//提示登录失败
showLoginFail(LoginActivity.this);
}
});
}
//保存登录数据
public void saveLoginData(){
//保存登录相关的数据,代码略过...
}
//提示登录成功
public void showLoginFail(Context context){
Toast.makeText(context, "登录失败", 0).show();
}
//提示登录失败
public void showLoginSuccess(Context context){
Toast.makeText(context, "登录成功", 0).show();
}
//检查输入的合法性
private boolean checkInput(String username, String password) {
boolean result = true;
//1.检查为空
if(TextUtils.isEmpty(username) || TextUtils.isEmpty(password)){
Toast.makeText(this, "用户名或者密码不能为空!", 0).show();
result = false;
}
//2.检查长度
if(username.length()!=11){
Toast.makeText(this, "用户名长度不正确!", 0).show();
result = false;
}
if(password.length()<5){
Toast.makeText(this, "密码长度不能小于5位!", 0).show();
result = false;
}
return result;
}
}
-
现在抽取出LoginModel,LoginView和LoginPresenter类,如下:
-
LoginView类:
/** * 负责登录模块相关的UI等所有UI相关逻辑 * @author lxj * */ public class LoginView { public void showLoginSuccess(Context context){ Toast.makeText(context, "登录成功", 0).show(); } public void showLoginFail(Context context){ Toast.makeText(context, "登录失败", 0).show(); } public void showInputNoNull(Context context){ Toast.makeText(context, "用户名和密码不能为空!", 0).show(); } public void showUsernameLengthError(Context context){ Toast.makeText(context, "用户名长度不正确!", 0).show(); } private ProgressDialog progressDialog; //显示登录中对话框 public void showLoginDialog(Context context){ if(progressDialog==null){ progressDialog = new ProgressDialog(context); progressDialog.setTitle("正在登录中..."); } progressDialog.show(); } //隐藏登录中对话框 public void hideLoginDialog(Context context){ if(progressDialog!=null){ progressDialog.dismiss(); } } }
-
LoginModel类如下:
/** * 负责处理登录相关数据的业务逻辑,并根据数据处理的状态和进度 * 暴露接口,通知外界 * @author lxj * */ public class LoginModel { private LoginModelObserver loginModelObserver; public void setLoginModelObserver(LoginModelObserver loginModelObserver) { this.loginModelObserver = loginModelObserver; } // 执行登录请求 public void execLogin(String username, String password) { if(!checkInput(username, password)){ return; } // 执行登录请求的伪代码 HttpHelper helper = new HttpHelper(); helper.execRequest("http://www.baidu.com", new HttpCallback() { @Override public void onSuccess() { // 需要UI展示,暴露接口 if(loginModelObserver!=null){ loginModelObserver.onLoginSuccess(); } // 保存登录相关数据,如登录的标记,用户的唯一标识 saveLoginData(); } @Override public void onFail() { // 需要UI展示,暴露接口 if(loginModelObserver!=null){ loginModelObserver.onLoginFail(); } } }); } // 保存登录数据 public void saveLoginData() { // 保存登录相关的数据,代码略过... } // 检查输入的合法性 private boolean checkInput(String username, String password) { boolean result = true; // 1.检查为空 if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) { if (loginModelObserver != null) { loginModelObserver.onInputNoNull(); } result = false; } // 2.检查长度 if (username.length() != 11) { if (loginModelObserver != null) { loginModelObserver.onUsernameLengthError(); } result = false; } return result; } // 定义数据处理状态的回调 public interface LoginModelObserver { // 输入数据不能为空的回调 void onInputNoNull(); // 用户名长度不正确的回调 void onUsernameLengthError(); //登录成功的回调 void onLoginSuccess(); //登录失败的回调 void onLoginFail(); } }
-
LoginPresenter类如下:
/** * 负责连接LoginView和LoginModel的表现层 * 具体就是调用LoginModel和LoginView的方法 * @author lxj * */ public class LoginPresenter implements LoginModelObserver{ private LoginModel loginModel; private LoginView loginView; private Context context; public LoginPresenter(Context context){ this.context = context; loginModel = new LoginModel(); loginView = new LoginView(); loginModel.setLoginModelObserver(this); } //登录的方法 public void login(String username, String password) { //调用loginModel封装好的方法 loginModel.execLogin(username, password); } @Override public void onInputNoNull() { //调用loginView展示UI的方法 loginView.showInputNoNull(context); } @Override public void onUsernameLengthError() { loginView.showUsernameLengthError(context); } @Override public void onLoginSuccess() { loginView.showLoginSuccess(context); } @Override public void onLoginFail() { loginView.showLoginFail(context); } }
-
最后LoginActivity实现如下:
public class LoginActivity extends ActionBarActivity implements OnClickListener{ EditText etUsername,etPassword; Button btn_login; private LoginPresenter loginPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); etUsername = (EditText) findViewById(R.id.et_username); etPassword = (EditText) findViewById(R.id.et_password); btn_login = (Button) findViewById(R.id.btn_login); //设置点击事件 btn_login.setOnClickListener(this); //与loginPresenter交互 loginPresenter = new LoginPresenter(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_login: String username = etUsername.getText().toString(); String password = etPassword.getText().toString(); loginPresenter.login(username,password); break; } } }
-
-
总结:MVP只是给我们提出了分层解耦的思想,并没有一个固定的实现。Google虽然出了官方的MVP实现示例,但是并没有太多人去跟随,很多公司在对Presenter层都有自己的理解,俊哥给给出的也是自己的理解,此处的案例相对来说比较规范,对MVP三层都有清晰的解耦和实现,在茫茫开源项目中,算是比较好的容易理解的上手项目了。