MVP概念:
1.Model:业务逻辑和实体模型
2.View:View通常来说是由Activity实现的,它会包含一个Presenter的引用,View要做的就只是在每次有接口调用的时候(比如按钮点击后)调用Presenter的方法。
3.主要作为沟通View和Model的桥梁,它从Model层检索数据后,返回给View层,但是不像MVC结构,因为它也可以决定与View层的交互操作。
MVP优缺点:
优点:
1. 降低耦合度,实现了Model和View真正的完全分离,可以修改View而不影响Modle
2. 模块职责划分明显,层次清晰(下面会介绍Bob大叔的Clean Architecture)
3. 隐藏数据
4. Presenter可以复用,一个Presenter可以用于多个View,而不需要更改Presenter的逻辑(当然是在View的改动不影响业务逻辑的前提下)
5. 利于测试驱动开发。以前的Android开发是难以进行单元测试的(虽然很多Android开发者都没有写过测试用例,但是随着项目变得越来越复杂,没有测试是很难保证软件质量的;而且近几年来Android上的测试框架已经有了长足的发展——开始写测试用例吧),在使用MVP的项目中Presenter对View是通过接口进行,在对Presenter进行不依赖UI环境的单元测试的时候。可以通过Mock一个View对象,这个对象只需要实现了View的接口即可。然后依赖注入到Presenter中,单元测试的时候就可以完整的测试Presenter应用逻辑的正确性。
6. View可以进行组件化。在MVP当中,View不依赖Model。这样就可以让View从特定的业务场景中脱离出来,可以说View可以做到对业务完全无知。它只需要提供一系列接口提供给上层操作。这样就可以做到高度可复用的View组件。
7. 代码灵活性
缺点:
1. Presenter中除了应用逻辑以外,还有大量的View->Model,Model->View的手动同步逻辑,造成Presenter比较笨重,维护起来会比较困难。
2. 由于对视图的渲染放在了Presenter中,所以视图和Presenter的交互会过于频繁。
3. 如果Presenter过多地渲染了视图,往往会使得它与特定的视图的联系过于紧密。一旦视图需要变更,那么Presenter也需要变更了。
4. 额外的代码复杂度及学习成本。
MVP小demo:模拟一个用户登录程序
1. 首先是用户bean对象 没什么好解释的
public class UserInfo {
private int userId;
private String username;
private String password;
public UserInfo(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
2. Model接口和它的实现类
/**
* Created by zlc on 2017/2/5.
* 定义登录,保存用户和读取用户信息的方法
*/
public interface IUserModel {
boolean saveUserInfo(int userId,String userName,String passWord);
UserInfo getUserInfo(int userId);
boolean login(String userName,String passWord);
}
/**
* Created by zlc on 2017/2/5.
* Model具体实现类 分别实现上述三个方法 实现具体逻辑
*/
public class UserModelImpl implements IUserModel {
private Map<Integer,UserInfo> userInfoMap;
public UserModelImpl(){
userInfoMap = new HashMap<>();
}
@Override
public boolean saveUserInfo(int userId, String userName, String passWord) {
UserInfo userInfo = new UserInfo(userName,passWord);
userInfoMap.put(userId,userInfo);
return true;
}
@Override
public UserInfo getUserInfo(int userId) {
return userInfoMap.get(userId);
}
@Override
public boolean login(String userName, String passWord) {
if("zlc".equals(userName) && "zlc123".equals(passWord)){
return true;
}else{
return false;
}
}
}
3. View接口定义
public interface IUserView {
int getUserId(); //获取用户输入的用户id
String getUserName(); //获取用户输入的用户名
String getPassWord(); //获取用户输入的密码
void setUserId(int userId); //这个主要是取用户信息数据回填用户id 一下同
void setUserName(String userName);
void setPassWord(String passWord);
}
4. Presenter定义:包含View接口和Model实现类的引用,Model层获取数据,返回给Vew层。
public class UserPresenter {
private IUserModel userModel;
private IUserView userView;
private Context mContext;
public UserPresenter(Context context,IUserView view){
userModel = new UserModelImpl();
this.mContext = context;
this.userView = view;
}
public boolean saveUserInfo(int userId,String userName,String passWord){
if(TextUtils.isEmpty(userId+"") || TextUtils.isEmpty(userName) || TextUtils.isEmpty(passWord)){
Toast.makeText(mContext, "用户ID 或者 用户名 或者 用户密码不能为空", Toast.LENGTH_SHORT).show();
return false;
}
return userModel.saveUserInfo(userId, userName, passWord);
}
public void getUserInfo(int userId){
if(userId==0){
Toast.makeText(mContext, "用户ID不能为0", Toast.LENGTH_SHORT).show();
return;
}
UserInfo userInfo = userModel.getUserInfo(userId);
if(userInfo==null){
Toast.makeText(mContext, "用户信息不存在", Toast.LENGTH_SHORT).show();
userView.setUserId(userId);
userView.setUserName("");
userView.setPassWord("");
return;
}
userView.setUserId(userId);
userView.setUserName(userInfo.getUsername());
userView.setPassWord(userInfo.getPassword());
}
public boolean login(String userName,String passWord){
return userModel.login(userName,passWord);
}
}
5. Activity实现,实现View接口,包含Presenter的应用,具体逻辑都是通过调用Presenter里面的方法去实现 减少Activity代码的长篇大论。
public class MainActivity extends AppCompatActivity implements IUserView,View.OnClickListener{
private UserPresenter userPresenter;
private EditText id_et_userid;
private EditText id_et_username;
private EditText id_et_pwd;
private Button id_btn_login;
private Button id_btn_save;
private Button id_btn_get;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
userPresenter = new UserPresenter(MainActivity.this,this);
initUI();
initListener();
}
private void initUI() {
id_et_userid = (EditText) findViewById(R.id.id_et_userid);
id_et_username = (EditText) findViewById(R.id.id_et_username);
id_et_pwd = (EditText) findViewById(R.id.id_et_pwd);
id_btn_login = (Button) findViewById(R.id.id_btn_login);
id_btn_save = (Button) findViewById(R.id.id_btn_save);
id_btn_get = (Button) findViewById(R.id.id_btn_get);
}
private void initListener() {
id_btn_get.setOnClickListener(this);
id_btn_save.setOnClickListener(this);
id_btn_login.setOnClickListener(this);
}
@Override
public int getUserId() {
int userId = !TextUtils.isEmpty(id_et_userid.getText().toString()) ? Integer.valueOf(id_et_userid.getText().toString()) : 0;
return userId;
}
@Override
public String getUserName() {
return id_et_username.getText().toString();
}
@Override
public String getPassWord() {
return id_et_pwd.getText().toString();
}
@Override
public void setUserId(int userId) {
id_et_userid.setText(userId+"");
}
@Override
public void setUserName(String userName) {
id_et_username.setText(userName);
}
@Override
public void setPassWord(String passWord) {
id_et_pwd.setText(passWord);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.id_btn_get:
userPresenter.getUserInfo(getUserId());
break;
case R.id.id_btn_save:
boolean save = userPresenter.saveUserInfo(getUserId(), getUserName(), getPassWord());
String saveStatus = save ? "成功" : "失败";
Toast.makeText(MainActivity.this, "保存"+saveStatus, Toast.LENGTH_SHORT).show();
break;
case R.id.id_btn_login:
boolean login = userPresenter.login(getUserName(), getPassWord());
String loginStatus = login ? "成功" : "失败";
Toast.makeText(MainActivity.this, "登录"+loginStatus, Toast.LENGTH_SHORT).show();
break;
}
}
}
6. 总结
MVP适合大项目,小项目反而会多了很多接口,不易理解,但是一旦项目很多,优点自是好处多多,写的不好的地方望多多见谅,本人菜鸟一枚,刚刚入坑mvp,学习学习,写个博客纯属做点笔记。
7. 源代码下载
http://download.csdn.net/detail/rjgcszlc/9752914
8. 联系方式:
qq:1509815887