Android:安卓学习笔记之MVP模式的简单理解和使用

article/2025/9/23 1:26:58

Android MVP模式的简单理解和使用

  • MVP模式
    • 1、 为什么使用MVP模式?
      • 1.1、实例说明
    • 2、一步步让你理解MVP
      • 2.1、MVP实现第一步, 将页面拆分为M/V/P三个模块
      • 2.2、 MVP实现第2步, 使用接口通信,进一步解耦
        • 2.2.1、MVP遵从的面向对象原则
      • 2.3、 从头到尾封装一个完整的MVP框架
        • 2.3.1、业务分析
        • 2.3.2、BaseInterface 与 Contract的概念
        • 2.3.3、从头到尾封装一个完整的MVP框架。
      • 2.4、 最终优化版
    • 3、具体实例
      • 3.1、MyUser
      • 3.2、BaseContract
      • 3.3、LandContract
      • 3.4、BaseMVPActivity
      • 3.5、LoginActivity
      • 3.6、LandPresenter
  • 参考

MVP模式

1、 为什么使用MVP模式?

为什么引入架构呢?引入架构的项目,必是到了一定的规模,也就是出现了一定程度的耦合与冗余,也一定意义上违反了面向对象的单一职责原则。

那么MVP解决的问题就很明显了, 那就是冗余、混乱、耦合重。此时抛开MVP不讲,如果要我们自己想办法去解决,如何来解决呢?

分而治之, 我们可能会想到,根据单一职责原则,Activity或Fragment或其他组件冗余了,那么必然要根据不同的功能模块,来划分出来不同的职责模块,这样也就遵循了单一职责的原则。站在前人的智慧上,或许很多人就想到了M(Model)V(View)C(Controller)。我们可以借鉴这一开发模式,来达到我们的目的,暂时将一个页面划分为

UI模块,也即View层Model模块,也即数据请求模块Logic模块, 司逻辑处理

这样划分首先职责分工就明确了,解决了混乱,冗余的问题。

  • 一个项目从分包,到分类,最后拆分方法实现,都是遵从单一职责;
  • 一个职责划分越具有原子性, 它的重用性就越好,当然这也要根据实际业务而定。比如以下代码:

1.1、实例说明

在这里插入图片描述

public class LoginActivity extends AppCompatActivity {EditText inputUserName;EditText inputPassword;Button btnLogin;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);btnLogin.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {final String userName = inputUserName.getText().toString();final String password = inputPassword.getText().toString();boolean isEmptyUserName = userName == null || userName.length() == 0;boolean isEmptyPassword = userName == null || userName.length() == 0;boolean isUserNameValid =Pattern.compile("^[A-Za-z0-9]{3,20}+$").matcher(userName   ).matches();boolean isPasswordValid = Pattern.compile("^[A-Za-z0-9]{3,20}+$").matcher(password ).matches()if (isEmptyPassword || isEmptyPassword) {Toast.makeText(LoginActivity.this, "请输入帐号密码", Toast.LENGTH_SHORT).show();} else {if (isUserNameValid && isPasswordValid) {new Thread(new Runnable() {@Overridepublic void run() {// ...登录请求boolean loginResult = false;if (loginResult) {Toast.makeText(LoginActivity.this, "登录成功", Toast.LENGTH_SHORT).show();} else {Toast.makeText(LoginActivity.this, "登录失败", Toast.LENGTH_SHORT).show();}}}).start();} else {Toast.makeText(LoginActivity.this, "帐号密码格式错误", Toast.LENGTH_SHORT).show();}}}});}
}

一个简单的登录, 包括点击事件获取登录信息判断是否空校验是否正确请求登录返回处理

  • 这样的代码结构混乱, 可读性差; 代码冗余,可重用性差;
  • 不同功能的代码糅合在一起, 耦合性高。这只是很简单的一个小功能。

上面说到, 面向对象的单一职责原则, 一个模块划分越具有原子性,也即划分越细,那么重用性就越高。如果我改成这样

public class LoginActivity extends AppCompatActivity {EditText inputUserName;EditText inputPassword;Button btnLogin;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);btnLogin.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {final String userName = getEditorText(inputUserName);final String password = getEditorText(inputPassword);if (isEmpty(userName) || isEmpty(password)) {showTips("请输入帐号密码");} else {if (isValid(userName) && isValid(password)) {// 登录doLogin(userName, password);} else {showTips("帐号密码格式错误");}}}});}private boolean isValid(String s) {return Pattern.compile("^[A-Za-z0-9]{3,20}+$").matcher(s).matches();}private boolean isEmpty(String s) {return s == null || s.length() == 0;}private String getEditorText(EditText et) {return et.getText().toString();}private void showTips(String tips) {Toast.makeText(LoginActivity.this, tips, Toast.LENGTH_SHORT).show();}private void doLogin(String username, String password) {new Thread(new Runnable() {@Overridepublic void run() {// ...登录请求boolean loginResult = false;// 更新UInotifyLoginResult(loginResult);}}).start();}private void notifyLoginResult(boolean loginResult) {if (loginResult) {showTips("登录成功");} else {showTips("登录失败");}}}

将源码方法进行拆分后, isEmpty, isValid, showTips…等,产生的结果有亮点:

  • 1) 方法拆分后,可重用性提高了
  • 2) 相比而言,浏览一遍,我能基本清楚onClick里做了什么,也就是架构清晰了

这就是单一职责原则的作用,提高可重用性, 减少代码冗余,开始露出清晰的思维脉络。

以上说明了单一职责的意义,以及带来的附加的益处。那么代码经过初步重构以后, 虽然更清晰了,消除了冗余,但是耦合的问题依旧。那怎么解决耦合问题呢?我们来看下半场

2、一步步让你理解MVP

MVP最难的难点之一: 如何正确划分各模块

  • Model很简单, 数据加载的界限很明确,很简单就划分出来了, 比如数据库操作, 比如文件查询, 比如网络请求,可以连带着异步操作一起拿出来,划分为单独的Model层。

View层与Presenter层交互性很频繁,很多人不清楚这一块代码算是View,还是Presenter

  • 首先, 单纯的逻辑实现必然是Presenter处理的;单纯的View初始化也必然是View处理的,如findView这些。

像登录模块,View与逻辑交错在一起,怎么区分呢 ?

首先Login功能大抵分为以下子功能:

1、取值, EditText帐号与密码(明确的View层,不涉及逻辑操作)
2、判空与校验 (Presenter但涉及View, 因为使用帐号与密码,通过传参的形式)
3、登录请求 (名副其实的Model, 处理明显在Presenter层)
4、更新UI (View层)

其实以上划分界限相对比较清晰,项目中难免遇到一些不好界限的,教你一招,难以划分的必然包含View也包含逻辑处理。那么第一步,

  • 原子性拆分,将View与逻辑处理单独拆分成不同的方法。View 的部分在View层, 处理的部分在Presenter层
  • 有一些Toast, Dialog等的划分,根据Context作区分。
    • 可以使用Application Context实现的,可以作为Presenter层; 必须使用Activity
      Context的,作为View层

那么明确了M V P的拆分,看一下拆分结果

2.1、MVP实现第一步, 将页面拆分为M/V/P三个模块

1、View 部分

public class LoginActivity extends AppCompatActivity {EditText inputUserName;EditText inputPassword;Button btnLogin;LoginPresenter presenter;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);presenter = new LoginPresenter(this);btnLogin.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {presenter.execureLogin(getEditorText(inputUserName), getEditorText(inputPassword));}});}private String getEditorText(EditText et) {return et.getText().toString();}public void showTips(String tips) {Toast.makeText(LoginActivity.this, tips, Toast.LENGTH_SHORT).show();}public void notifyLoginResult(boolean loginResult) {if (loginResult) {showTips("登录成功");} else {showTips("登录失败");}}}

2、Model部分

public class LoginModel {private Handler handler;public LoginModel() {handler = new Handler();}public interface OnLoginCallback {void onResponse(boolean success);}public void login(String username, String password, final OnLoginCallback callback) {new Thread(new Runnable() {@Overridepublic void run() {// ...请求接口boolean result = true; // 假设这是接口返回的结果callback.onResponse(result);}}).start();}
}

Presenter部分

public class LoginPresenter {private LoginModel model;private LoginActivity activity;private String verifyMsg;public LoginPresenter(LoginActivity activity) {this.activity = activity;model = new LoginModel();}public void execureLogin(String username, String password) {boolean verifyBefore = verifyBeforeLogin(username, password);if (verifyBefore) {// 校验通过,请求登录model.login(username, password, new LoginModel.OnLoginCallback() {@Overridepublic void onResponse(boolean success) {// 登录结果activity.notifyLoginResult(success);}});} else {// 校验失败,提示activity.showTips(verifyMsg);}}private boolean verifyBeforeLogin(String username, String password) {boolean isEmpty = isEmpty(username) || isEmpty(password);boolean isValid = isValid(username) && isValid(password);if (isEmpty) {verifyMsg = "请输入帐号或密码";return false;}if (isValid) {return true;}verifyMsg = "帐号或密码错误";return false;}private boolean isValid(String s) {return Pattern.compile("^[A-Za-z0-9]{3,20}+$").matcher(s).matches();}private boolean isEmpty(String s) {return s == null || s.length() == 0;}
}

通过以上代码可以看出:

  • 1、Toast提示, 更新登录状态等, 都拆分在View层;
  • 2、校验与登录则拆分在Presenter层;
  • 3、网络请求则拆分到了Model层。

这样每一层都只处理本层的业务,从大的方向上进行了单一职责拆分,从而整体符合单一职责原则。

根据MVP将页面拆分为了3层,单一职责的原则我们已经完全符合了。但是仔细看,忽然发现相互之间还存在依赖,解耦效果并不是那么理想。那我们要思考了,是什么原因导致耦合尚在?

那就是对象持有,看看我们的项目

  • Presenter持有View(Activity)对象,同时持有Model对象
  • View持有Presenter对象

MVP是怎么解决对象持有问题的?

  • 面向接口编程

2.2、 MVP实现第2步, 使用接口通信,进一步解耦

对于面向对象设计来讲, 利用接口达到解耦目的已经是人尽皆知的了。 这次改动很小,把对象持有改为接口持有即可。

  • View持有Presenter对象改为持有Presenter接口
  • Presenter持有View对象改为持有View接口

既然持有接口,肯定要在View与Presenter分别实现供外部调用的接口。

  • View供Presenter调用的方法有notifyLoginResultshowTips
  • Presenter供View调用的方法有executeLogin

那么先来实现接口如何?看代码

Presenter接口

public interface IPresenter {/*** 执行登录** @param username* @param password*/void executeLogin(String username, String password);
}

View接口

public interface IView {/*** 更新登录结果** @param loginResult*/void notifyLoginResult(boolean loginResult);/*** Toast提示** @param tips*/void showTips(String tips);
}

接口的作用是对外部提供一种供外部调用的规范。因此这里我们把外部需要调用的方法抽象出来,加入到接口中。接口有了,且接口代表的是View或Presenter的实现,所以分别实现它们。看代码

Presenter实现接口

public class LoginPresenter implements IPresenter {private LoginModel model;private LoginActivity activity;private String verifyMsg;public LoginPresenter(LoginActivity activity) {this.activity = activity;model = new LoginModel();}@Overridepublic void executeLogin(String username, String password) {boolean verifyBefore = verifyBeforeLogin(username, password);if (verifyBefore) {// 校验通过,请求登录model.login(username, password, new LoginModel.OnLoginCallback() {@Overridepublic void onResponse(boolean success) {// 登录结果activity.notifyLoginResult(success);}});} else {// 校验失败,提示activity.showTips(verifyMsg);}}private boolean verifyBeforeLogin(String username, String password) {boolean isEmpty = isEmpty(username) || isEmpty(password);boolean isValid = isValid(username) && isValid(password);if (isEmpty) {verifyMsg = "请输入帐号或密码";return false;}if (isValid) {return true;}verifyMsg = "帐号或密码错误";return false;}private boolean isValid(String s) {return Pattern.compile("^[A-Za-z0-9]{3,20}+$").matcher(s).matches();}private boolean isEmpty(String s) {return s == null || s.length() == 0;}
}

View实现接口

public class LoginActivity extends AppCompatActivity implements IView{EditText inputUserName;EditText inputPassword;Button btnLogin;LoginPresenter presenter;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);presenter = new LoginPresenter(this);btnLogin.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {presenter.executeLogin(getEditorText(inputUserName), getEditorText(inputPassword));}});}private String getEditorText(EditText et) {return et.getText().toString();}@Overridepublic void showTips(String tips) {Toast.makeText(LoginActivity.this, tips, Toast.LENGTH_SHORT).show();}@Overridepublic void notifyLoginResult(boolean loginResult) {if (loginResult) {showTips("登录成功");} else {showTips("登录失败");}}}

在接口中提供对外部调用的方法,然后分别在View和Presenter中实现它们。接口与实现都有了,还记得我们的目的是什么吗?是把持有的对象替换为接口,撸起来,看代码

// 这是View持有的接口,在onCreate中初始化的对象由原来的LoginPresenter改为了IPresenter。
public class LoginActivity extends AppCompatActivity implements IView{EditText inputUserName;EditText inputPassword;Button btnLogin;IPresenter presenter;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);presenter = new LoginPresenter(this);...
}// 这是Presenter持有的接口,在构造中由原来的LoginActivity改为了IViewpublic class LoginPresenter implements IPresenter {private LoginModel model;private String verifyMsg;private IView activity;public LoginPresenter(IView activity) {this.activity = activity;model = new LoginModel();}...

2.2.1、MVP遵从的面向对象原则

1) 单一职责

  • 每个模块只负责该模块的本职工作,不越界。 如View负责UI初始化与更新, Model负责数据查询与异步,至于逻辑判断,业务实现,放心的扔给Presenter中就好了。

2) 面向接口通信

  • 对象的持有是造成耦合的本质原因之一,因此要达到解耦的目的,替换为接口持有最是合适不过。

2.3、 从头到尾封装一个完整的MVP框架

2.3.1、业务分析

这里以登录为例。先来分析下业务:

1、EditText取得输入的UserName与Password

2、校验(包括判断是否是空的, 是否符合输入规范比如不允许输入特殊字符)

3、校验通过, 执行登录请求; 不通过,直接提示错误

4、登录请求

5、根据登录结果,提示登录成功或失败

6、伴随着登录结果更新UI

2.3.2、BaseInterface 与 Contract的概念

MVP引入了BaseInterface 与Contract的概念。如果单纯的mvp,可能很多人都理解,但是加上这两个概念,加深了理解难度。

base-interface 就是我们常用的base的概念,目的就是规范统一的操作。比如显示一个Toast, 判断网络是否连接,跳转动画等,我们都放在BaseActivity中,因为所有的Activity都需要这些。接口的继承也是这个目的。如登录功能

  • 1) 我们需要一个Presenter,于是有了LoginPresenter
  • 2)我们需要一个LoginPresenter的接口,为View层提供调用,于是有了ILoginPresenter
  • 3)无论登录,还是注册,还有其他功能,所有的Presenter都需要一个功能start, 于是有了IPresenter
    • IPresenter提供了一个所有Presenter接口共有的操作,就是start,也即初始化的加载 Contract的概念

这个概念的引入只是为了统一管理一个页面的View和Presenter接口。每个页面对应

  • 1个View(Activity或Fragment), 一个IView(View接口)
  • 1个Presenter,一个IPresenter(Presenter接口)
  • 1个Contract(一个包含View接口和Presenter接口的接口)

public interface LoginContract {interface View {void notifyLoginResult();}interface Presenter {void login(String username, String password);}}

2.3.3、从头到尾封装一个完整的MVP框架。

1) 首先来思考,我们最先定义的应该是什么? 当然是公共接口。

View的公共接口(MVP-Samples中的IView)没有公共的操作,我们定义一个空的接口,用于统一规范。

public interface IView {
}

Presenter的公共接口(MVP-Samples中的IPresenter)也没有公共的操作,在mvp提供的samples中是带了一个start的,但是这里不需要。

  • 为什么呢?因为我们还要来一个BasePresenter。所以我们还是定义一个空的接口,用于统一规范。
public interface IPresenter {
}

以上两个接口,是用于给View与Presenter的接口继承的,注意,不是View或Presenter本身继承。因为它定义的是接口的规范, 而接口才是定义的类的规范。

2)定义的接口要继承IView与IPresenter, 而且由Contract统一管理

public interface LoginContract {interface View extends IView {// View中的2个功能:// 1) 取得登录需要的username, password # 不需要对Presenter层提供调用// 2) 提示错误信息, 提示登录结果 # 需要Presenter层调用,因为校验和登录都是在Presenter层的// 因此2)是View层提供的对外方法,需要在接口中定义/*** 提示一个Toast** @param msg*/void showToast(String msg);}interface Presenter extends IPresenter {// Presenter中的2个功能:// 1) 校验 # 看你怎么写,既可以在View层中调用校验方法,也可以在Presenter层中,这里定义为直接在Presenter中校验,彻底和View解耦// 2) 登录 # 先执行校验,再执行登录,需要在View层点击登录时调用// 因此2)是Presenter对外层提供的方法,需要在接口中定义/*** 登录操作** @param username* @param password*/void login(String username, String password);}
}

以上Contract(称之为功能仓库)分别定义了View与Presenter接口,并添加了接口的定义过程分析。

3) 接口定义完成了,下一步是什么呢? 肯定是实现接口,加入功能吧。定义Presenter与View分别实现接口,加入对应功能。

public class LoginActivity extends AppCompatActivity implements LoginContract.View {LoginContract.Presenter mPresenter;Button loginBtn;EditText etUser, etPwd;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);mPresenter = new LoginPresenter(this);loginBtn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {mPresenter.login(etUser.getText().toString(), etPwd.getText().toString());}});}@Overridepublic void showToast(String msg) {Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();}*********************************************************************************************************
public class LoginPresenter implements LoginContract.Presenter {LoginContract.View mView;public LoginPresenter(LoginContract.View mView) {this.mView = mView;}@Overridepublic void login(String username, String password) {// 校验直接放在登录流程boolean isVerifySuccessully = verifyLoginInfo();if (isVerifySuccessully) {// 请求登录LoginModel.requestLogin(username, password, new LoginModel.RequestCallback() {@Overridepublic void onResponse(boolean result) {if (result) {// 提示登录成功mView.showToast("登录成功");} else {// 提示登录失败mView.showToast("登录失败");}}});} else {// 校验失败,提示错误mView.showToast("无效的帐号或密码");}}private boolean verifyLoginInfo() {// 这里校验登录信息// 校验帐号,密码是否为空// 校验帐号,密码是否符合要求return true;}
}

注意:LoginActivity在相对的生命周期中需要销毁Presenter引用,由于后面会封装,这里没加。

  • 可能出现内存泄漏,如果model层数据没有回调完成,activity退出,以为presenter持有当前activity,所以gc不了,出现内存泄漏

走到这一步基本就是一个完整的MVP开发模式了,从划分层次到接口通信,其实还是挺简单的,不是么?下面继续来优化这个框架,我们考虑以下几个问题:

  • 每个Activity或者Fragment都要初始化或管理Presenter,累不累?
  • 同样的,每个Presenter都要管理View,累不累?

2.4、 最终优化版

Presenter基类抽取,公共元素有哪些 :

  • Presenter公共元素,其实主要有两个: ContextView接口。
    • 注意:Presenter不要传入Activity的Context;
      • 如果需要用到Activity的Context,那么Presenter层就不单纯了。那么只能是Application的Context。
      • 我们获取Application Context的方式有两种,AppContext(你的Application)的静态获取 和 Activity的getApplicationContext。这里使用传入的Application Context吧!

很多网上View的获取是定义一个AttachView的方法, 这里使用在构造中直接传入。


public abstract class BasePresenter<AttachView extends IView> {private Context mContext;private AttachView mView;public BasePresenter(Context context, AttachView view) {if (context == null) {throw new NullPointerException("context == null");}mContext = context.getApplicationContext();mView = view;}/*** 获取关联的View** @return*/public AttachView getAttachedView() {if (mView == null) {throw new NullPointerException("AttachView is null");}return mView;}/*** 获取关联的Context** @return*/public Context getContext() {return mContext;}/*** 清空Presenter*/public void clearPresenter() {mContext = null;mView = null;}/*** View是否关联** @return*/public boolean isViewAttached() {return mView != null;}/*** 网络是否连接** @return*/public boolean isNetworkConnected() {if (mContext == null) {throw new NullPointerException("mContext is null");}return NetworkHelper.isNetworkConnected(mContext);}public abstract void start();public abstract void destroy();
}

以上是我们抽取的Presenter基类。实现了:

1、初始化时绑定View接口,并在clear时清除接口
2、自动获取ApplicationContext(还是建议不这样,直接传Application的Context) 3、View状态判定
4、网络连接判断(因为Presenter中执行网络请求比较频繁,你可以根据业务自定义多个方法)
5、satrt初始化方法与destroy销毁方法(结合后面的MVPCompatActivity自动销毁)

注意:在使用View时,请先判断View状态,否则View异常销毁时会报NullPoiterException如果有线程或者Handler一定要在destroy中销毁,避免造成内存泄漏

for Activity

/*** MVP - Activity基类*/
public abstract class MVPCompatActivity<T extends BasePresenter> extends RootActivity {protected T mPresenter;@Overrideprotected void onStart() {super.onStart();if (mPresenter == null) {mPresenter = createPresenter();}mPresenter.start();}@Overrideprotected void onStop() {super.onStop();mPresenter.clearPresenter();mPresenter = null;}@Overridepublic void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {super.onSaveInstanceState(outState, outPersistentState);mPresenter.clearPresenter();mPresenter = null;}/*** 创建一个Presenter** @return*/protected abstract T createPresenter();}

for Fragment


public abstract class MVPCompatFragment<T extends BasePresenter> extends RootFragment {protected T mPresenter;@Overridepublic void onStart() {super.onStart();if (mPresenter == null) {mPresenter = createPresenter();}mPresenter.start();}@Overridepublic void onStop() {super.onStop();if (mPresenter != null) {mPresenter.clearPresenter();mPresenter = null;}}protected abstract T createPresenter();}

for Layout


public abstract class MVPCompatLayout<T extends BasePresenter> extends RootLayout {protected T mPresenter;public MVPCompatLayout(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}@Overrideprotected void onAttachedToWindow() {super.onAttachedToWindow();mPresenter = createPresenter();}@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();if (mPresenter != null) {mPresenter.clearPresenter();mPresenter = null;}}protected abstract T createPresenter();
}

for Adapter


public abstract class MVPCompatRecyclerAdapter<T, P extends BasePresenter> extends RootRecyclerAdapter<T> {protected P mPresenter;public MVPCompatRecyclerAdapter(Context context, List data) {super(context, data);}protected abstract P createPresenter();@Overridepublic void onViewAttachedToWindow(RecyclerViewHolder holder) {super.onViewAttachedToWindow(holder);mPresenter = createPresenter();}@Overridepublic void onViewDetachedFromWindow(RecyclerViewHolder holder) {super.onViewDetachedFromWindow(holder);if (mPresenter != null) {mPresenter.clearPresenter();mPresenter = null;}}
}

通过继承以上View的Base, 可以自由实现初始化以及销毁。轻松实现MVP。

最后,补充的RootActivity, 作为一个Base的Activity,是根据不同的业务决定里面的内容的,因此这里很少


public abstract class RootActivity extends AppCompatActivity {protected Context mContext;protected Context mAppContext;private View mContentView;private Bundle mBundleObj;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);mAppContext = getApplicationContext();mContext = this;mContentView = getLayoutInflater().inflate(getLayoutRes(), null);setContentView(mContentView);ButterKnife.bind(this);init();}protected abstract int getLayoutRes();protected abstract void init();/*** findViewById** @param resId* @param <T>* @return*/protected <T extends View> T $(int resId) {return (T) findViewById(resId);}/*** Toast** @param toast*/protected void showToast(String toast) {Toast.makeText(this, toast, Toast.LENGTH_SHORT).show();}/*** get a bundle from reuse.** @return*/protected Bundle obtainBundle() {if (mBundleObj == null) {mBundleObj = new Bundle();} else {mBundleObj.clear();}return mBundleObj;}}

3、具体实例

在这里插入图片描述

3.1、MyUser

public class MyUser extends BmobUser {private String image;private String gender;private String age;public String getImage() {return image;}public void setImage(String image) {this.image = image;}public String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}public String getAge() {return age;}public void setAge(String age) {this.age = age;}
}

3.2、BaseContract

public interface BaseContract {interface BasePresenter<T> {void attachView(T view);void detachView();}interface BaseView {void onSuccess();void onFailure(Throwable e);}
}

3.3、LandContract

public interface LandContract extends BaseContract {interface View extends BaseContract.BaseView {void landSuccess(MyUser user);}interface Presenter extends BaseContract.BasePresenter<View>{/*** 用户登陆*/void login(String username, String password);/*** 用户注册*/void signup(String password, String mail);}
}

3.4、BaseMVPActivity

public abstract class BaseMVPActivity<T extends BaseContract.BasePresenter> extends BaseActivity{protected T mPresenter;protected abstract T bindPresenter();@Overrideprotected void processLogic() {attachView(bindPresenter());}private void attachView(T presenter){mPresenter = presenter;mPresenter.attachView(this);}@Overrideprotected void onDestroy() {super.onDestroy();mPresenter.detachView();}
}

3.5、LoginActivity

public class LoginActivity extends BaseMVPActivity<LandContract.Presenter>implements LandContract.View, View.OnFocusChangeListener, View.OnClickListener {private OwlView mOwlView;private EditText usernameET;private EditText passwordET;private EditText rpasswordET;private TextView signTV;private TextView forgetTV;private Button loginBtn;//是否是登陆操作private boolean isLogin = true;@Overrideprotected int getLayoutId() {return R.layout.activity_user_land;}@Overrideprotected void initWidget() {super.initWidget();mOwlView=findViewById(R.id.land_owl_view);usernameET=findViewById(R.id.login_et_username);passwordET=findViewById(R.id.login_et_password);rpasswordET=findViewById(R.id.login_et_rpassword);signTV=findViewById(R.id.login_tv_sign);forgetTV=findViewById(R.id.login_tv_forget);loginBtn=findViewById(R.id.login_btn_login);}@Overrideprotected void initClick() {super.initClick();passwordET.setOnFocusChangeListener(this);rpasswordET.setOnFocusChangeListener(this);signTV.setOnClickListener(this);forgetTV.setOnClickListener(this);loginBtn.setOnClickListener(this);}@Overridepublic void onFocusChange(View v, boolean hasFocus) {if (hasFocus) {mOwlView.open();} else {mOwlView.close();}}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.login_btn_login:  //buttonif (isLogin) {login();  //登陆} else {sign();  //注册}break;case R.id.login_tv_sign:  //signif (isLogin) {//置换注册界面signTV.setText("登录");loginBtn.setText("注册");rpasswordET.setVisibility(View.VISIBLE);usernameET.setVisibility(View.GONE);} else {//置换登陆界面signTV.setText("注册");loginBtn.setText("登录");usernameET.setVisibility(View.VISIBLE);rpasswordET.setVisibility(View.GONE);}isLogin = !isLogin;break;case R.id.login_tv_forget:  //忘记密码showForgetPwDialog();break;default:break;}}/*** 执行登陆动作*/public void login() {String username = usernameET.getText().toString();String password = passwordET.getText().toString();if (username.length() == 0 || password.length() == 0) {SnackbarUtils.show(mContext, "用户名或密码不能为空");return;}ProgressUtils.show(this, "正在登陆...");mPresenter.login(username, password);}/*** 执行注册动作*/public void sign() {String email = usernameET.getText().toString();String password = passwordET.getText().toString();String rpassword = rpasswordET.getText().toString();if (email.length() == 0 || password.length() == 0 || rpassword.length() == 0) {SnackbarUtils.show(mContext, "请填写必要信息");return;}if (!StringUtils.checkEmail(email)) {SnackbarUtils.show(mContext, "请输入正确的邮箱格式");return;}if (!password.equals(rpassword)) {SnackbarUtils.show(mContext, "两次密码不一致");return;}ProgressUtils.show(this, "正在注册...");usernameET.setText(email);mPresenter.signup(password,email);}/***********************************************************************/@Overrideprotected LandContract.Presenter bindPresenter() {return new LandPresenter();}@Overridepublic void landSuccess(MyUser user) {ProgressUtils.dismiss();if (isLogin) {setResult(RESULT_OK, new Intent());Intent intent = new Intent(this, MainActivity1.class);startActivity(intent);finish();} else {Toast.makeText(this, "注册成功", Toast.LENGTH_SHORT).show();//置换登陆界面signTV.setText("注册");loginBtn.setText("登录");rpasswordET.setVisibility(View.GONE);usernameET.setVisibility(View.VISIBLE);isLogin=true;}Log.i(TAG,user.toString());}@Overridepublic void onSuccess() {ProgressUtils.dismiss();}@Overridepublic void onFailure(Throwable e) {ProgressUtils.dismiss();SnackbarUtils.show(mContext, e.getMessage());Log.e(TAG,e.getMessage());}/*** 显示忘记密码对话框*/public void showForgetPwDialog() {new MaterialDialog.Builder(this).title("找回密码").inputType(InputType.TYPE_CLASS_TEXT).input("请输入注册邮箱", null, (dialog, input) -> {String inputStr = input.toString();if (input.equals("")) {SnackbarUtils.show(mContext, "内容不能为空!");} else if(!StringUtils.checkEmail(inputStr)) {Toast.makeText(LoginActivity.this,"请输入正确的邮箱格式", Toast.LENGTH_LONG).show();} else {//找回密码BmobUser.resetPasswordByEmail(input.toString(), new UpdateListener() {@Overridepublic void done(BmobException e) {if (e == null) {ToastUtils.show(mContext, "重置密码请求成功,请到邮箱进行密码重置操作");} else {ToastUtils.show(mContext, "重置密码请求失败,请确认输入邮箱正确!");}}});}}).positiveText("确定").negativeText("取消").show();}}

3.6、LandPresenter

public class LandPresenter extends RxPresenter<LandContract.View> implements LandContract.Presenter{private String TAG="LandPresenter";@Overridepublic void login(String username, String password) {MyUser.loginByAccount(username, password, new LogInListener<MyUser>() {@Overridepublic void done(MyUser myUser, BmobException e) {if(e == null) {mView.landSuccess(myUser);}else {String error=e.toString();if(error.contains("incorrect")){Toast.makeText(getApplicationContext(), "账号或者密码错误!", Toast.LENGTH_SHORT).show();mView.onSuccess();}else {mView.onFailure(e);}}}});}@Overridepublic void signup(String password, String mail) {MyUser myUser =new MyUser();myUser.setPassword(password);myUser.setEmail(mail);myUser.signUp(new SaveListener<MyUser>() {@Overridepublic void done(MyUser myUser, BmobException e) {if(e == null)mView.landSuccess(myUser);else{String error = e.toString();if(error.contains("already")){Toast.makeText(getApplicationContext(), "邮箱已经被注册,请重新填写!", Toast.LENGTH_SHORT).show();mView.onSuccess();}else {mView.onFailure(e);}}}});}
}

参考

1、一步步带你精通MVP
2、移动架构这么多,如何一次搞定所有
3、Android 架构MVC MVP MVVM+实例


http://chatgpt.dhexx.cn/article/PNex2GXY.shtml

相关文章

MVP开发模式解析

前言 由于项目里同事用到MVP开发模式&#xff0c;我看了几篇关于 MVP 的文章&#xff0c;对其有了基本的了解之后&#xff0c;便照猫画虎进行了开发&#xff0c;之后便再也没接触过 MVP。 最近空闲读了些MVP的文章&#xff0c;受益匪浅&#xff0c;于是打算写一篇关于MVP开发…

MVP模式的优缺点

MVP模式是MVC的一个演化版本&#xff0c;全称是Model view Presenter。 MVP能够有效的降低View的复杂性&#xff0c;避免业务逻辑被塞进View中,使得View变成一个混乱的“大泥坑”。 MVP模式会解除View与Model的耦合&#xff0c;同时又带来了良好的可扩展性&#xff0c;可测试…

Android MVP开发模式 google 官方Mvp架构详解(转)

Google官方MVP Sample代码解读 关于Android程序的构架, 当前最流行的模式即为MVP模式, Google官方提供了Sample代码来展示这种模式的用法.Repo地址: android-architecture.本文为阅读官方sample代码的阅读笔记和分析. 官方Android Architecture Blueprints [beta]:Android在如…

MVP模式的相关知识

MVP 是从经典的模式MVC演变而来&#xff0c;它们的基本思想有相通的地方&#xff1a;Controller/Presenter负责逻辑的处理&#xff0c;Model提供数据&#xff0c;View负责显示。作为一种新的模式&#xff0c;MVP与MVC有着一个重大的区别&#xff1a;在MVP中View并不直接使用Mod…

【iOS】MVP模式

文章目录 什么是MVP模式&#xff1f;图解 从MVC到MVP苹果的MVC为何要从MVC到MVP?MVP MVP模式下的工程MVP模式的优缺点 什么是MVP模式&#xff1f; MVP模式是MVC模式的一个演化版本&#xff0c;MVP全称Model-View-Presenter。&#xff08;关于MVC模式可见这篇文章&#xff09; …

浅谈安卓中的MVP模式

端午放假&#xff0c;天气下雨&#xff0c;于是乎在家撸一下博客&#xff0c;本篇博客将为大家解析MVP模式在安卓中的应用。 本文将从以下几个方面对MVP模式进行讲解&#xff1a; 1. MVP简介 2. 为什么使用MVP模式 3. MVP模式实例 4. MVP中的内存泄露问题 1. MVP简介&…

Android MVP模式 入门

1.前言 近些年来&#xff0c;Android架构模式有很多&#xff0c;我们比较熟知的有MVC&#xff0c;MVP以及MVVM&#xff0c;目前Android市场中使用最多的应该是MVP架构&#xff0c;虽然MVVM结合DataBing看似更加方便&#xff0c;但在一般公司中使用的还是比较少。其实模式这种东…

MVP模式实例解释

为什么在UI层包含太多的逻辑是很糟糕的&#xff1f;在既不手动运行应用程序&#xff0c;也不维护丑陋的自动执行UI组件的UI运行者脚本(runner script)的情况下&#xff0c;位于应用程序UI层中的代码是非常难于调试的。虽然这本身就是一个很大的问题&#xff0c;一个更大的问题是…

Android开发之MVP模式

前言&#xff1a;在之前的开发中一直用的是mvc模式搭建的项目&#xff0c;所以我对于mvp也一直只是停留在理论和demo阶段上。正好现在的项目是被小伙伴借助dragger搭建的mvp模式的结构&#xff0c;所以就想着总结整理一下mvp模式的东西并写出来&#xff0c;也算是作为自己使用了…

MVP模式与MVC模式

源地址&#xff1a;http://www.cnblogs.com/cuihongyu3503319/archive/2009/01/09/1372820.html MVP模式与MVC模式(转) MVP 是从经典的模式MVC演变而来&#xff0c;它们的基本思想有相通的地方&#xff1a;Controller/Presenter负责逻辑的处理&#xff0c;Model提供数据&#x…

MVP模式从入门到精通

首先附上自己写的一个MVP的demo&#xff0c;这是一个很标准的MVP&#xff0c;Github地址如下&#xff1a; https://github.com/SilasGao/MVPDemo 首先MVP 是从经典的MVC架构演变而来&#xff0c;那我们是不是要先说下何为MVC模式&#xff1f; 系统C/S(Client/Server)三层架构模…

MVP模式使用示例详解

什么是MVP模式? 这个MVP可不是腾讯游戏《王者荣耀》中的MVP。我们今天要讨论的MVP其实同MVC一样&#xff0c;是一种编程模式和思想&#xff0c;也许更准确地讲是一种架构。 MVP和MVC的区别 提到MVP模式&#xff0c;大家自然避免不了要和我们以前常用的MVC模式进行对…

MVP设计模式

Model–view–presenter (MVP) 是model–view–controller (MVC)设计模式派生出来的。MVP经常用来创建用户界面。 presenter是作为一个“中间人”的角色存在。在MVP中&#xff0c;所有页面显示逻辑都会被推送到presenter。 以下这张图是MVC模式的&#xff1a; MVP与MVC有着一…

Android中用到的MVP模式

参考&#xff1a;android架构设计—mvp模式封装 很简单&#xff0c;M&#xff1a;数据&#xff0c; V:界面&#xff0c; P:一个使唤数据(M)和界面(V)干活的大管家。 特点&#xff1a;在P的管理下&#xff0c;P可以直接支配V和M做一些事情。但是V&#xff0c;与M&#xff0c;你…

Android MVP模式 简单易懂的介绍方式

Android MVP Pattern Android MVP 模式1 也不是什么新鲜的东西了&#xff0c;我在自己的项目里也普遍地使用了这个设计模式。当项目越来越庞大、复杂&#xff0c;参与的研发人员越来越多的时候&#xff0c;MVP 模式的优势就充分显示出来了。 导读&#xff1a;MVP模式是MVC模式在…

Android MVP模式详解

一、MVP概述 MVP&#xff0c;全称 Model-View-Presenter&#xff0c;即模型-视图-层现器。 提到MVP&#xff0c;就必须要先介绍一下它的前辈MVC&#xff0c;因为MVP正是基于MVC的基础发展而来的。两个之间的关系也是源远流长。 MVC&#xff0c;全称Model-View-Controller&am…

浅谈安卓MVP模式

本篇博文通过对google官方demo&#xff1a;https://github.com/googlesamples/android-architecture/tree/todo-mvp/的理解&#xff0c;用自己的demo更好的讲解mvp的概念&#xff0c;帮助大家如何针对一个Activity页面去编写针对MVP风格的代码。 一、MVP模式介绍 随着UI创建技…

简单易懂 MVP 模式

Android MVP 模式 [1] 也不是什么新鲜的东西了&#xff0c;我在自己的项目里也普遍地使用了这个设计模式。当项目越来越庞大、复杂&#xff0c;参与的研发人员越来越多的时候&#xff0c;MVP 模式 的优势就充分显示出来了。 MVP 模式是 MVC 模式在 Android 上的一种变体&#…

深入浅出——MVP模式

由于公司里的架构模式用到MVP&#xff0c;觉得自己还不够熟悉&#xff0c;决定在此理一理&#xff0c;并给大家一起总结下。 一 MVP模式介绍 MVP全称Model View Presenter。 MVP能够有效的降低View的复杂性&#xff0c;避免业务逻辑被塞进View中&#xff0c;防止View的代码变…

MVP模式简单讲解,通俗易懂

了解 MVP 和 MVC 的区别 https://baike.baidu.com/item/MVP/3714550?fraladdin 什么是MVP&#xff1a; MVP 是 MVC 的变种&#xff0c;其实是一种升级。要说 MVP 就要说说 MVC&#xff0c;在 MVC 中 Activity 其实是 View层级&#xff0c;但是通常在使用中 Activity即是View…