android设计模mvp,Android MVP设计模式总结

MVP设计模式从提出至今也有不短的时间了,大家应该或多或少使用过MVP模式开发项目,或者至少听说过MVP设计模式,不同的人对其有不同的理解,今天就来说说我所理解的MVP设计模式。

MVC

说起MVP就不得不提MVC设计模式,MVP模式是从MVC模式中演化出来的。MVC包含以下三种组件:

控制器(Controller)- 负责转发请求,对请求进行处理。

视图(View) - 界面设计人员进行图形界面设计。

模型(Model) - 程序员编写程序应有的功能(实现算法等等)、数据库专家进行数据管理和数据库设计(可以实现具体的功能)。

他们之间的联系为:

6e91a861491b

维基百科中的MVC

View负责显示图形界面与获取用户事件输入,事件输入之后交给Controller进行逻辑控制处理,Controller调用Model进行数据处理(如:操作数据库读取数据等),当Model中数据发生改变后会通知Controller,Controller控制View刷新界面。同时View也可以直接将数据交给Model处理,并监听Model中的数据改变来刷新界面。(详细介绍可参考维基百科中的MVC设计模式)

MVP

MVP设计模式同样包含三种组件:

Model 定义用户界面所需要被显示的数据模型,一个模型包含着相关的业务逻辑。

View 视图为呈现用户界面的终端,用以表现来自 Model 的数据,和用户命令路由再经过 Presenter 对事件处理后的数据。

Presenter 包含着组件的事件处理,负责检索 Model 获取数据,和将获取的数据经过格式转换与 View 进行沟通。

但他与MVC设计模式不同的是,MVP模式实现了View与Model的完全解耦。结构图如下:

6e91a861491b

MVP设计模式结构图

可以看到相比MVC模式MVP各组件之间的分工更明确,View只负责UI展示和用户事件输入,Presenter负责协调View和Model的沟通,Model负责数据操作,数据操作的结果只需要反馈给Presenter。

这样设计的优点也显而易见:

分类了视图、逻辑、数据层,降低了个模块之间的耦合性,并实现了视图层和数据层的完全解耦。

个组件之间通过接口实现交互,可以很方便的进行单元测试。

利于代码的复用,不同的Activity可以复用同一个Presenter,同样的不同Presenter也可以复用同一个Model进行数据处理。

代码更加灵活

对于大项目来说,方便不同开发人员进行模块化开发协作。

代码实现

上面说了这么多还得最终落实到代码上,下面将通过MVP模式实现简单的登录功能。

6e91a861491b

效果图

UML类图(不太熟练,如有错误,望不吝赐教):

6e91a861491b

登录功能的UML类图

项目结构:

6e91a861491b

登录功能MVP模式项目结构

分别创建View、Presenter、Model三个包存放三种组件的实现类。

UserBean:

用户存放用户信息的实体类,添加一个变量用于模拟不同的登录状态。

public class UserBean {

private String userName;

private String password;

//模拟不同的登录状态

private String loginResultType = "1";

private String token;

public UserBean() {

}

public UserBean(String userName, String password) {

this.userName = userName;

this.password = password;

}

public UserBean(String userName, String password, String loginResultType, String token) {

this.userName = userName;

this.password = password;

this.loginResultType = loginResultType;

this.token = token;

}

//Getter和Setter代码不贴了

@Override

public String toString() {

return "UserName=" + userName

+ "\n Password=" + password

+ "\n token=" + token;

}

}

View:

ILoginView接口定义:

public interface ILoginView {

void showLoading();

void hideLoading();

/**

* 登录成功

* @param userBean 用户类

*/

void showLoginSuccess(UserBean userBean);

/**

* 显示登录失败信息

* @param message 失败信息

*/

void showFailureMessage(String message);

/**

* 显示登录错误信息

* @param message 错误信息

*/

void showErrorMessage(String message);

}

LoginActivity实现:

public class LoginActivity extends AppCompatActivity implements ILoginView{

private static final String TAG = LoginActivity.class.getSimpleName();

private ILoginPresenter loginPresenter; //login Presenter

private RadioGroup loginResultRg; //模拟登录状态的RadioGroup

private EditText userNameEt; //用户名

private EditText passwordEt; //密码

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

loginResultRg = findViewById(R.id.login_result_rg);

userNameEt = findViewById(R.id.user_name_et);

passwordEt = findViewById(R.id.password_et);

loginPresenter = new LoginPresenter(this);

}

/**

* 登录事件

* @param view 事件触发View

*/

public void login(View view) {

UserBean userBean = new UserBean();

userBean.setUserName(userNameEt.getText().toString().trim());

userBean.setPassword(passwordEt.getText().toString().trim());

//通过RadioButton的选中状态模拟不同的登录状态

switch (loginResultRg.getCheckedRadioButtonId()){

case R.id.success_rb:

userBean.setLoginResultType("1");

break;

case R.id.failure_rb:

userBean.setLoginResultType("2");

break;

case R.id.error_rb:

userBean.setLoginResultType("3");

break;

}

loginPresenter.getLoginData(userBean);

}

@Override

public void showLoading() {

Log.d(TAG, "showLoading");

Toast.makeText(LoginActivity.this, "showLoading", Toast.LENGTH_SHORT).show();

}

@Override

public void hideLoading() {

Log.d(TAG, "hideLoading");

Toast.makeText(LoginActivity.this, "hideLoading", Toast.LENGTH_SHORT).show();

}

@Override

public void showLoginSuccess(UserBean userBean) {

Log.d(TAG, "showLoginSuccess user Information " + userBean.toString());

Toast.makeText(LoginActivity.this, "showLoginSuccess userName=" + userBean.toString(), Toast.LENGTH_SHORT).show();

}

@Override

public void showFailureMessage(String message) {

Log.d(TAG, "showFailureMessage message= " + message);

Toast.makeText(LoginActivity.this, "showFailureMessage msg=" + message, Toast.LENGTH_SHORT).show();

}

@Override

public void showErrorMessage(String message) {

Log.d(TAG, "showErrorMessage message=" + message);

Toast.makeText(LoginActivity.this, "showErrorMessage msg=" + message, Toast.LENGTH_SHORT).show();

}

}

XML就不贴了需要的可以去下载demo看。

Model:

ILoginModel接口定义:

public interface ILoginModel {

/**

* 登录操作

* @param param 参数

*/

void doLogin(UserBean param, LoginCallBack loginCallBack);

/**

* 登录状态回调

*/

public interface LoginCallBack{

/**

* 登录成功

* @param data 返回数据

*/

void onSuccess(UserBean data);

/**

* 调用登录接口时,接口调用成功,但是

* 因用户名错误、登录失效等后台控制逻辑导致的登录失败

* @param data 失败原因

*/

void onFailure(String data);

/**

* 接口调用失败

* 网络不通

* 接口超时

* 404、500等原因

* @param error 失败原因

*/

void onError(String error);

/**

* 接口请求结束,包括上面三中情况

* 设置此方法通常是进行hideLoading等操作

*/

void onComplete();

}

}

LoginModel:

public class LoginModel implements ILoginModel {

private Handler handler;

public LoginModel(){

handler = new Handler(Looper.getMainLooper());

}

@Override

public void doLogin(final UserBean param, final LoginCallBack loginCallBack) {

loginCallBack.onComplete();

//模拟登录延迟操作

handler.postDelayed(new Runnable() {

@Override

public void run() {

switch (param.getLoginResultType()){

case "1":

param.setToken("登录成功");

loginCallBack.onSuccess(param);

break;

case "2":

loginCallBack.onFailure("用户名或密码错误");

break;

case "3":

loginCallBack.onError("接口超时");

break;

}

}

}, 3000);

}

}

Presenter:

ILoginPresenter接口定义:

public interface ILoginPresenter {

/**

* 获取登录数据

* @param param 参数

*/

void getLoginData(UserBean param);

}

LoginPresenter实现:

public class LoginPresenter implements ILoginPresenter {

private ILoginView loginView;

private ILoginModel loginModel;

public LoginPresenter(ILoginView loginView){

this.loginView = loginView;

loginModel = new LoginModel();

}

@Override

public void getLoginData(UserBean userBean) {

loginView.showLoading();

loginModel.doLogin(userBean, new ILoginModel.LoginCallBack() {

@Override

public void onSuccess(UserBean data) {

loginView.showLoginSuccess(data);

}

@Override

public void onFailure(String data) {

loginView.showFailureMessage(data);

}

@Override

public void onError(String error) {

loginView.showErrorMessage(error);

}

@Override

public void onComplete() {

loginView.hideLoading();

}

});

}

}

一句话总结一下登录流程:

用户点击登录按钮触发登录操作,View也就是LoginActivity调用Presenter的getLoginData()方法,开启登录逻辑,Presenter调用Model的doLogin方法,执行具体的登录操作。Model将登录结果通过回调反馈给Presenter,Presenter控制View进行相应的UI显示。

另一种实现:

上面这种实现是最基本的实现,下面介绍另一种实现,将IView、IModel、IPresenter中的接口封装到contract中,并实现相关的基类方便其他模块扩展实现MVP模式。

UML类图:

6e91a861491b

MVP类图

项目结构

6e91a861491b

MVP项目结构图

Contract类

添加了Contract包,用于存放不同模块的协约类,用于将上一种实现方式中分散在IView、IModel、IPresenter中的接口统一归纳、统一管理。

public class LoginContract {

/**

* 登录View接口

*/

public interface ILoginView {

void showLoading();

void hideLoading();

/**

* 登录成功

* @param userBean 用户类

*/

void showLoginSuccess(UserBean userBean);

/**

* 显示登录失败信息

* @param message 失败信息

*/

void showFailureMessage(String message);

/**

* 显示登录错误信息

* @param message 错误信息

*/

void showErrorMessage(String message);

}

/**

* 登录Presenter

*/

public interface ILoginPresenter {

/**

* 获取登录数据

* @param param 参数

*/

void getLoginData(UserBean param);

}

/**

* 登录Model

*/

public interface ILoginModel {

/**

* 登录操作

* @param param 参数

*/

void doLogin(UserBean param, LoginCallBack loginCallBack);

}

}

View

IView:

定义了一个IView接口类,此类中抽象出所有View共同的方法,如:showLoading、hideLoading等,还有个作用就是为所有的View定义统一的接口方便之后在BasePresenter中进行泛型。

public interface IView {

//定义统一的空接口

}

BaseActivity:

定义基类BaseActivity,封装一些通用方法便于其他模块的Activity进行扩展。注意在此类中实现了IView接口,所以在之后的Activity中不在需要实现IView接口。

public abstract class BaseActivity

extends AppCompatActivity implements IView{

//定义Presenter的泛型进行约束

protected P mPresenter;

@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

if(initLayout() instanceof Integer){

setContentView((Integer) initLayout());

} else if(initLayout() instanceof View){

setContentView((View) initLayout());

} else{

throw new IllegalArgumentException("initLayout() 应该返回Int或者View类型对象");

}

//初始化Presenter

mPresenter = initPresenter();

//Presenter与View进行绑定

mPresenter.attachView(this);

create();

}

@Override

protected void onDestroy() {

super.onDestroy();

//Presenter与View解除绑定

mPresenter.detachView();

}

/** 初始化Presenter的抽象方法 */

protected abstract P initPresenter();

/** 初始化布局的抽象方法 */

protected abstract Object initLayout();

/** Activity OnCreate之后的create抽象方法 */

protected abstract void create();

}

LoginActivity:

LoginActivity需要继承其父类BaseActivity并实现Login协约类中的View接口。

ublic class LoginActivity extends BaseActivity implements LoginContract.ILoginView {

private static final String TAG = LoginActivity.class.getSimpleName();

private RadioGroup loginResultRg; //模拟登录状态的RadioGroup

private EditText userNameEt; //用户名

private EditText passwordEt; //密码

@Override

protected LoginPresenter initPresenter() {

return new LoginPresenter();

}

@Override

protected Object initLayout() {

return R.layout.activity_main;

}

@Override

protected void create() {

loginResultRg = findViewById(R.id.login_result_rg);

userNameEt = findViewById(R.id.user_name_et);

passwordEt = findViewById(R.id.password_et);

}

/**

* 登录事件

* @param view 事件触发View

*/

public void login(View view) {

UserBean userBean = new UserBean();

userBean.setUserName(userNameEt.getText().toString().trim());

userBean.setPassword(passwordEt.getText().toString().trim());

//通过RadioButton的选中状态模拟不同的登录状态

switch (loginResultRg.getCheckedRadioButtonId()){

case R.id.success_rb:

userBean.setLoginResultType("1");

break;

case R.id.failure_rb:

userBean.setLoginResultType("2");

break;

case R.id.error_rb:

userBean.setLoginResultType("3");

break;

}

mPresenter.getLoginData(userBean);

}

@Override

public void showLoading() {

Log.d(TAG, "showLoading");

Toast.makeText(LoginActivity.this, "showLoading", Toast.LENGTH_SHORT).show();

}

@Override

public void hideLoading() {

Log.d(TAG, "hideLoading");

Toast.makeText(LoginActivity.this, "hideLoading", Toast.LENGTH_SHORT).show();

}

@Override

public void showLoginSuccess(UserBean userBean) {

Log.d(TAG, "showLoginSuccess user Information " + userBean.toString());

Toast.makeText(LoginActivity.this, "showLoginSuccess userName=" + userBean.toString(), Toast.LENGTH_SHORT).show();

}

@Override

public void showFailureMessage(String message) {

Log.d(TAG, "showFailureMessage message= " + message);

Toast.makeText(LoginActivity.this, "showFailureMessage msg=" + message, Toast.LENGTH_SHORT).show();

}

@Override

public void showErrorMessage(String message) {

Log.d(TAG, "showErrorMessage message=" + message);

Toast.makeText(LoginActivity.this, "showErrorMessage msg=" + message, Toast.LENGTH_SHORT).show();

}

}

Model

IModel:

统一的接口类IModel,作用同上IView接口。

public interface IModel {

}

LoginModel:

实现IModel和LoginContract.ILoginModel接口

public class LoginModel implements IModel,LoginContract.ILoginModel {

private Handler handler;

public LoginModel(){

handler = new Handler(Looper.getMainLooper());

}

@Override

public void doLogin(final UserBean param, final LoginCallBack loginCallBack) {

loginCallBack.onComplete();

//模拟登录延迟操作

handler.postDelayed(new Runnable() {

@Override

public void run() {

switch (param.getLoginResultType()){

case "1":

param.setToken("登录成功");

loginCallBack.onSuccess(param);

break;

case "2":

loginCallBack.onFailure("用户名或密码错误");

break;

case "3":

loginCallBack.onError("接口超时");

break;

}

}

}, 3000);

}

}

Presenter

IPresenter:

统一的Presenter接口,定义了绑定和解绑View的方法。

public interface IPresenter {

void attachView(IView view);

void detachView();

}

BasePresenter:

定义View与Model的泛型进行约束,实现上面的接口。

public abstract class BasePresenter implements IPresenter{

protected V mView;

protected M mModel;

public BasePresenter(){

mModel = initModel();

}

@Override

public void attachView(IView view) {

mView = (V) view;

}

/**

*初始化Moel的抽象方法

*/

protected abstract M initModel();

@Override

public void detachView() {

mView = null;

mModel = null;

}

}

LoginPresenter:

继承基类,实现接口,没什么好说的。

public class LoginPresenter extends BasePresenter implements LoginContract.ILoginPresenter {

@Override

public void getLoginData(UserBean userBean) {

mView.showLoading();

mModel.doLogin(userBean, new LoginContract.ILoginModel.LoginCallBack() {

@Override

public void onSuccess(UserBean data) {

mView.showLoginSuccess(data);

}

@Override

public void onFailure(String data) {

mView.showFailureMessage(data);

}

@Override

public void onError(String error) {

mView.showErrorMessage(error);

}

@Override

public void onComplete() {

mView.hideLoading();

}

});

}

@Override

protected LoginModel initModel() {

return new LoginModel();

}

}

至此关于MVP就介绍完了,并扩展了一种MVP的实现方式,实现方式并不是固定的,你可以根据自己对MVP的理解和项目需要自行实现MVP设计模式。

[1]文中引用部分均来自中文维基百科]1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值