Android MVP模式深入实践探索(一),我浪费了自己职业生涯的黄金5年

说起MVP就不得不提起MVC, 因为MVP的是在MVC的基础上优化而来的:

Android MVP模式深入实践探索(一),我浪费了自己职业生涯的黄金5年

MVC角色说明:

| 角色 | 职责 |

| :-- | :-- |

| View | 视图界面层,与用户发生交互,接收用户输入的请求转发给Controller处理 |

| Controller | 接收View的请求, 从视图层获取数据,执行业务逻辑,并调用Model层进行数据存取 |

| Model | 执行数据存取的业务逻辑,并根据业务模型通知视图层更新(一般是通过观察者模式) |

MVP角色说明:

| 角色 | 职责 |

| :-- | :-- |

| View | 视图界面层,与用户发生交互,接收用户输入的请求转发给Presenter处理 |

| Presenter | 接收View的请求, 从视图层获取数据,执行业务逻辑,并调用Model层进行数据存取,同时会调用View层的接口将数据更新到视图 |

| Model | 执行数据存取的业务逻辑,并将数据返回给Presenter层 |

可以看到MVP与MVC最大的区别就是MVP解耦了View层和Model层,两者不直接发生交互而是通过P层,而在MVC中Model还是会跟View层发生交互的。

传统的MVC模式更加适合于大型项目的开发,对于小型项目应用MVC反而显得臃肿繁琐,就像我们以前经常在Activity中最喜欢写的代码一样:UI处理、网络请求、数据存取等全部业务都放在Activity中来完成,这就像一个大杂烩,此时的Activity几乎兼顾了MVC中的所有角色,实际上在我看来它此时根本就不具备任何模式可言。而MVP模式的出现将臃肿的部分分解开来,恰好适合于小型的应用程序,比如移动端的应用,并且对单元测试也比较友好,所以大家都在提倡用这个模式进行开发。

按照惯例,还是从一个简单的登录页面来演示MVP的基本使用

《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享

(几乎我看过的所有关于MVP的文章貌似都是拿登录页面来入门的。。)

布局页面:

Android MVP模式深入实践探索(一),我浪费了自己职业生涯的黄金5年

布局activity_login.xml代码:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”

android:layout_width=“match_parent”

android:layout_height=“match_parent”

android:orientation=“vertical”

android:gravity=“center”

<EditText

android:id="@+id/edit_user_name"

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:inputType=“text”

android:maxLines=“1”

android:hint=“请输入用户名”

android:textSize=“16sp”

android:textColor="@color/black" />

<EditText

android:id="@+id/edit_user_password"

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:inputType=“textPassword”

android:maxLines=“1”

android:hint=“请输入密码”

android:textSize=“16sp”

android:textColor="@color/black" />

<Button

android:id="@+id/btn_login"

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:layout_marginTop=“20dp”

android:text=“登录”

android:textSize=“16sp”

android:textColor="@color/black"

/>

LoginActivity代码:

public class LoginActivity extends Activity implements View.OnClickListener {

private EditText mUserNameEdit;

private EditText mUserPasswordEdit;

private Button mLoginBtn;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_login);

initView();

}

private void initView() {

mUserNameEdit = (EditText) findViewById(R.id.edit_user_name);

mUserPasswordEdit = (EditText) findViewById(R.id.edit_user_password);

mLoginBtn = (Button) findViewById(R.id.btn_login);

mLoginBtn.setOnClickListener(this);

}

@Override

public void onClick(View v) {

switch (v.getId()) {

case R.id.btn_login:

//TODO

break;

default:

break;

}

}

}

如果是传统的MVC的话,到这一步基本就结束了,接下来就开始在LoginActivity中搞事情作死了。。

如果使用MVP的话,接下来我们需要建立几个package来存放对应的角色:

Android MVP模式深入实践探索(一),我浪费了自己职业生涯的黄金5年

其中view包下存放activity、fragment等UI控件,iview包下存放presenter与view进行交互的接口类,我们把LoginActivity放入view/activity包中:

Android MVP模式深入实践探索(一),我浪费了自己职业生涯的黄金5年

其他包下面的情况:

Android MVP模式深入实践探索(一),我浪费了自己职业生涯的黄金5年

在MVP中为了解耦Presenter跟View和Model的交互都通过接口进行,所以新建一个跟LoginActivity交互的接口类ILoginView

public interface ILoginView {

/** 获取输入框的登录用户名 */

String getUserName();

/** 获取输入框的用户密码 */

String getUserPassword();

/** 显示Toast提醒 */

void showToast(String msg);

/** 登录成功的UI处理 */

void onLoginSuccess();

/** 登录失败的UI处理 */

void onLoginFail();

/** 显示加载中弹窗 */

void showProgressDialog();

/** 隐藏加载中弹窗 */

void hideProgressDialog();

/** 获取当前UI页面的上下文 */

Context getContext();

}

view接口类中定义的基本都是一些UI数据或者显示UI控件的方法,然让LoginActivity实现这个接口:

public class LoginActivity extends Activity implements ILoginView, View.OnClickListener {

private EditText mUserNameEdit;

private EditText mUserPasswordEdit;

private Button mLoginBtn;

public ProgressDialog mProgressDialog;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_login);

initView();

}

private void initView() {

mUserNameEdit = (EditText) findViewById(R.id.edit_user_name);

mUserPasswordEdit = (EditText) findViewById(R.id.edit_user_password);

mLoginBtn = (Button) findViewById(R.id.btn_login);

mLoginBtn.setOnClickListener(this);

}

@Override

public void onClick(View v) {

switch (v.getId()) {

case R.id.btn_login:

//TODO

break;

default:

break;

}

}

@Override

public String getUserName() {

return mUserNameEdit.getText().toString();

}

@Override

public String getUserPassword() {

return mUserPasswordEdit.getText().toString();

}

@Override

public void showToast(String msg) {

ToastUtils.showToast(this, msg);

}

@Override

public void onLoginSuccess() {

ToastUtils.showToast(this, “登录成功”);

//跳转首页

gotoHomeActivity();

finish();

}

@Override

public void onLoginFail() {

ToastUtils.showToast(this, “登录失败”);

}

@Override

public void showProgressDialog() {

if (mProgressDialog == null) {

mProgressDialog = DialogUtils.showSpinningProgressDialog(this, “正在登录中…”, false);

} else {

if (!mProgressDialog.isShowing()) {

mProgressDialog.show();

}

}

}

@Override

public void hideProgressDialog() {

if (mProgressDialog != null) {

mProgressDialog.dismiss();

}

}

@Override

public Context getContext() {

return this;

}

/** 跳转首页 */

private void gotoHomeActivity() {

Intent intent = new Intent(this, MainActivity.class);

startActivity(intent);

}

}

View层到此就完事了,接下来实现PresenterModel角色的接口和实现类

Presenter接口和实现类:

public interface ILoginPresenter {

/** 登录操作逻辑处理 */

void login();

/** 登录成功逻辑处理 */

void onLoginSuccess();

/** 登录失败逻辑处理 */

void onLoginFail(String errMsg);

}

public class LoginPresenter implements ILoginPresenter {

private ILoginView mLoginView;

private ILoginModel mLoginModel;

public LoginPresenter(ILoginView loginView) {

mLoginView = loginView;

mLoginModel = new LoginModelImpl(this);

}

@Override

public void login() {

// 对用户名和密码的校验逻辑,这里只简单判空,实际可以加更多校验

if (TextUtils.isEmpty(mLoginView.getUserName())) {

mLoginView.showToast(“请输入用户名”);

return;

}

if (TextUtils.isEmpty(mLoginView.getPassword())) {

mLoginView.showToast(“请输入密码”);

return;

}

//判断网络是否可用

if (!NetUtils.checkNetState(mLoginView.getContext())) {

mLoginView.showToast(“当前无网络连接,请检查网络”);

return;

}

//显示登录进度弹窗

mLoginView.showProgressDialog();

//调用model层发起登录请求

mLoginModel.sendLoginRequest(mLoginView.getUserName(), mLoginView.getPassword());

}

/** 登陆成功 */

@Override

public void onLoginSuccess() {

//隐藏登录进度弹窗

mLoginView.hideProgressDialog();

//回调View层接口

mLoginView.onLoginSuccess();

}

/** 登陆失败 */

@Override

public void onLoginFail(String errMsg) {

//隐藏登录进度弹窗

mLoginView.hideProgressDialog();

//回调View层接口

mLoginView.onLoginFail(errMsg);

}

}

可以看到在LoginPresenter的实现类当中分别持有了ILoginViewILoginModel两个接口,LoginPresenter通过这两个接口分别与LoginActivity和LoginModel进行交互。其中ILoginView变量是通过构造函数传递进来的,而ILoginModel则是在构造函数内部创建的。

Model接口和实现类:

public interface ILoginModel {

/** 发起登录请求 */

void sendLoginRequest(String userName, String password);

}

public class LoginModelImpl implements ILoginModel {

private static final String API_LOGIN = “/mobile/login”;

private ILoginPresenter mPresenter;

上一篇:POJ-2942 Knights of the Round Table


下一篇:设计模式---迭代器模式