- 什么是MVP模式
- MVP跟MVC的区别
- MVP模式在Android里的应用
什么是MVP模式
M : Model
V : View
P : Presenter
MVP把Activity中的UI逻辑抽象成View接口,把业务逻辑抽象成Presenter接口,Model类还是原来的Model。
Model负责数据的处理,View负责页面的展示,而Presenter则负责完成View与Model之间的逻辑跟交互,Presenter的出现可以将View和Model完全独立开,在android开发中的体现就是activity仅用于显示界面和交互,不参与模型结构和逻辑
借用一张图:(来自鸿洋的博客)
MVP跟MVC的区别
在说区别之前先解释下什么是耦合性(百度):
耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。
我们写代码心中也会一直叨叨着低耦合,高聚合
,MVP的出现确实让我们的代码耦合度降低。犊子扯完,接下来简述MVP跟MVC的区别:
借用一张图(来自:这里):
在MVC
中,View可以直接访问Model,但是Model不依赖于View,所以View不可避免要进行大量的逻辑处理跟数据交互,这样想要去修改View是十分费事费力的,而在Android中Activity充当了View+Controll的角色,所以当一个页面复杂的时候一个Activity的代码会有几千行,这样维护的成本会非常高。
在MVP
中,Presenter完全将View跟Model进行了分离,Presenter跟具体的View是没有直接联系的,而是通过定义好的接口进行交互,这样我们可以保持View在变化的同时Presenter不变,在View中有Presenter的引用,在Presenter中持有对于View跟Model的interface引用,整个工作流程如下:
MVP模式在Android里的应用
我用MVP模式写了一个注册的简单Demo来演示MVP模式在Android中的应用:
目录结构
效果演示
需求分析
流程非常简单:代码实现如下
View需要进行的操作
public interface IRegisterView {
void clearState();//清除输入错误提示
void showProgress();//显示进度条
void hideProgress();//隐藏进度条
void hideKeyboard();//隐藏键盘
String getUserName();//获取输入框输入的用户名
String getPassword();//获取输入框输入的密码
String getConfirmPassword();//获取输入框输入的确认密码
TextInputLayout getNameWrapper();//获取TextInputLayout用来显示错误信息
TextInputLayout getPasswordWrapper();//获取TextInputLayout用来显示错误信息
TextInputLayout getConfirmPasswordWrapper();//获取TextInputLayout用来显示错误信息
void navigateToSuccessPage(User user);//跳转到注册成功的页面
}
View的具体实现
public class RegisterActivity extends AppCompatActivity implements IRegisterView {
private Button registerBtn;
private TextInputLayout nameWrapper, passwordWrapper, passwordConfirmWrapper;
private RegisterPresenter registerPresenter;
private ProgressDialog mProgressDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.register_layout);
registerBtn = (Button) findViewById(R.id.btn_register);
nameWrapper = (TextInputLayout) findViewById(R.id.input_name_wrapper);
passwordWrapper = (TextInputLayout) findViewById(R.id.input_password_wrapper);
passwordConfirmWrapper = (TextInputLayout) findViewById(R.id.input_password_confirm_wrapper);
registerBtn.setOnClickListener(mOnclickListener);
registerPresenter = new RegisterPresenter(this);
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setMessage("注册中,请稍后...");
}
private final View.OnClickListener mOnclickListener = new View.OnClickListener() {
@Override
public void onClick(View view) {
registerPresenter.register();
}
};
@Override
public void clearState(){
if(nameWrapper != null){
nameWrapper.setErrorEnabled(false);
}
if(passwordWrapper != null){
passwordWrapper.setErrorEnabled(false);
}
if(passwordConfirmWrapper != null){
passwordConfirmWrapper.setErrorEnabled(false);
}
}
@Override
public void showProgress() {
if(mProgressDialog != null && !mProgressDialog.isShowing()){
mProgressDialog.show();
}
}
@Override
public void hideProgress() {
if(mProgressDialog != null && mProgressDialog.isShowing()){
mProgressDialog.dismiss();
}
}
@Override
public void hideKeyboard() {
Utils.hideKeyboard(this);
}
@Override
public String getUserName() {
if(nameWrapper != null) {
return nameWrapper.getEditText().getText().toString();
}
return null;
}
@Override
public String getPassword() {
if(passwordWrapper != null){
return passwordWrapper.getEditText().getText().toString();
}
return null;
}
@Override
public String getConfirmPassword() {
if(passwordConfirmWrapper != null){
return passwordConfirmWrapper.getEditText().getText().toString();
}
return null;
}
@Override
public TextInputLayout getNameWrapper() {
return nameWrapper;
}
@Override
public TextInputLayout getPasswordWrapper() {
return passwordWrapper;
}
@Override
public TextInputLayout getConfirmPasswordWrapper() {
return passwordConfirmWrapper;
}
@Override
public void navigateToSuccessPage(User user) {
Intent mIntent = new Intent(this,MainActivity.class);
mIntent.putExtra("user",user);
startActivity(mIntent);
}
}
Model需要进行的操作
public interface IRegisterModel {
void saveUser(String userName,String userPwd,onRegisterFinishedListener onRegisterFinishedListener);//存储用户信息
User getUser();//获取用户信息
interface onRegisterFinishedListener{//需要Presenter来实现这个接口对注册结果进行处理
void registerSuccess(User user);
void registerFailed();
}
}
Model具体操作
public class RegisterModelImp implements IRegisterModel {
User mUser ;
@Override
public void saveUser(String userName, String userPwd, onRegisterFinishedListener onRegisterFinishedListener) {
if(userName != null && userPwd != null){
mUser = new User(userName,userPwd);
onRegisterFinishedListener.registerSuccess(mUser);
}else{
onRegisterFinishedListener.registerFailed();
}
}
@Override
public User getUser() {
return mUser;
}
}
Presenter的操作
public class RegisterPresenter implements IRegisterModel.onRegisterFinishedListener{
IRegisterView iRegisterView;
IRegisterModel registerModel;
public RegisterPresenter(IRegisterView iRegisterView){
this.iRegisterView = iRegisterView;
registerModel = new RegisterModelImp();
}
public void register(){
iRegisterView.clearState();
if(Utils.isEmptyName(iRegisterView.getUserName())){
iRegisterView.getNameWrapper().setError("用户名不能为空");
}else if(!Utils.invalidPassword(iRegisterView.getPassword())){
iRegisterView.getPasswordWrapper().setError("密码长度不得低于6位");
}else if(!Utils.invalidConfirmPassword(iRegisterView.getPassword(), iRegisterView.getConfirmPassword())){
iRegisterView.getConfirmPasswordWrapper().setError("两次密码输入不一致");
}else{
doRegister(iRegisterView.getUserName(), iRegisterView.getPassword());
}
}
/**
* 立即注册
*/
private void doRegister(final String userName, final String userPwd){
iRegisterView.hideKeyboard();
iRegisterView.showProgress();
Handler mHandler = new Handler();
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
iRegisterView.hideProgress();
registerModel.saveUser(userName,userPwd,RegisterPresenter.this);
}
},2000);
}
@Override
public void registerSuccess(User user) {//注册成功
iRegisterView.navigateToSuccessPage(user);
}
@Override
public void registerFailed() {//注册失败
//TODO
}
}
Bean(由于需要传输Bean所以实现了Parcelable接口)
public class User implements Parcelable{
private int userId;
private String name;
private String password;
public User(){}
public User(String name,String password){
this.name = name;
this.password = password;
}
public User(Parcel parcel){
userId = parcel.readInt();
name = parcel.readString();
password = parcel.readString();
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "UserName = "+this.getName()+"\nUserPassword = "+this.getPassword();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(userId);
dest.writeString(name);
dest.writeString(password);
}
public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>(){
@Override
public User createFromParcel(Parcel source) {
return new User(source);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
}
登录成功展示的页面
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
Intent resultIntent = getIntent();
if(resultIntent != null){
TextView mTextView = (TextView)findViewById(R.id.tv_register_result);
User mUser = resultIntent.getParcelableExtra("user");
if(mUser != null){
mTextView.setText(mUser.toString());
}
}
}
个人总结: 为什么要使用MVP?
1,提高代码维护性
2,代码清晰
3,项目结构清晰
最后:如果APP业务逻辑比较简单的话完全没有必要去使用MVP,不要为了使用MVP而使用MVP,一切都是以业务的复杂度来取舍