Android MVP模式简单介绍:以一个登陆流程为例
老的项目用的MVC的模式,最近完成了全部重构成MVP模式的工作,虽然比较麻烦,好处是代码逻辑更加清楚、简洁,流程更加清晰,对于后续版本迭代维护都挺方便。
对于一些想要学习MVP模式的同学来讲,百度搜出来的好多都没法直接转化为项目里可以直接用的东西,所以这里正好拿出自己项目里已经用了的,你们可以直接用到自己的项目里。当然,不可能把所有项目代码在这里放出来,所以就拿登陆的流程出来,这个比较合适也比较常用。
1、先看下包结构:
model:放一些bean类,以及网络处理类RetrofitManager,ServiceHelper(封装的网络请求类)等
view:放UI层需要实现的逻辑
presenter:放一些业务逻辑相关的接口及实现类

2、进入正题
首先,以登陆流程为例,简单画下流程图:

然后开始划分对应三个层的逻辑:
presenter:作为登录页面,涉及的业务逻辑有:记住密码,登录,保存登录之后获取的Token
public interface ILoginPresenter {
void rememberPassword(String account,String pwd);
void login(String phoneNum,String pwd);
void saveToken(String token);
}
LoginPresenterImpl:负责具体登陆逻辑及view层业务的调用,持有view层对象引用:iLoginView
public class LoginPresenterImpl implements ILoginPresenter {
private static final String TAG = "LoginPresenterImpl";
private String mMd5Pwd;
ILoginView mILoginView;
private Context mContext;
public LoginPresenterImpl(ILoginView iLoginView, Context context) {
this.mILoginView = iLoginView;
this.mContext = context;
}
@Override
public void rememberPassword(String account, String pwd) {
SPUtils.put(mContext, "remember_password", true);
SPUtils.put(mContext, "phoneNum", account);
SPUtils.put(mContext, "password", pwd);
}
@Override
public void login(String phoneNum, String pwd) {
if (TextUtils.isEmpty(phoneNum)) {
mILoginView.loginResult(false, Constant.PHONENUM_NULL);return;
}
if (!Utils.isMobileNO(phoneNum)) {
mILoginView.loginResult(false, Constant.PHONENUM_FALSE);return;
}
if (TextUtils.isEmpty(pwd)) {
mILoginView.loginResult(false, Constant.PWD_NULL);return;
}
mMd5Pwd = Utils.encrypt(pwd);
LogUtils.d(TAG, "pwd:" + pwd + "------------ mMd5Pwd:" + mMd5Pwd);
//判断网络是否可用
if (!Utils.isNetAvail()) {
mILoginView.loginResult(false, Constant.INTERNET_FAILED);
LogUtils.d(TAG, "网络不可用");
return;
}
//发起网络请求,查看手机号和密码是否正确
ServiceHelper.callEntity(RetrofitManager.getInstance().createReq(Login.class).getLoginData(phoneNum, mMd5Pwd), LoginBean.class, new OnResponseLisner<LoginBean>() {
@Override
public void onSuccess(LoginBean info) {
int mUid = info.getData().getUID();
String token = info.getData().getToken();
saveToken(token);
mILoginView.loginResult(true, String.valueOf(mUid));
}
@Override
public void onError(String errorMsg) {
mILoginView.loginResult(false, errorMsg);
}
});
}
@Override
public void saveToken(String token) {
if (!TextUtils.isEmpty(token)) {
//存储String值
SPUtils.put(mContext, "Token", token);
}
}
view:登陆结果的处理展示(由具体实现类MainActivity实现对应的方法)
public interface ILoginView {
void loginResult(Boolean result, String msg);
}
model:服务器返回的数据bean类
public class LoginBean {
public boolean Success;
public int Code;
public String ErrorMsg_zh;
public String ErrorMsg_en;
public DataBean Data;
public int ServerTime;
public String LogId;
public static class DataBean {
public int UID;
public String Name;
public String Phone;
public String Email;
public String FacePic;
public String Token;
}
最后,看下完整的登陆页面MainActivity(ILoginView实现类)的代码:
public class LoginActivity extends BaseActivity implements ILoginView {
@BindView(R.id.et_phoneNum)
EditText mEtPhoneNum;
@BindView(R.id.et_pwd)
EditText mEtPwd;
@BindView(R.id.iv_phoneNumClear)
ImageView mPhoneNumClear;
@BindView(R.id.iv_pwdClear)
ImageView mPwdClear;
@BindView(R.id.cb_checkbox)
CheckBox mCheckBox;
@BindView(R.id.btn_login)
Button mBtnLogin;
@BindView(R.id.avi_loading)
AVLoadingIndicatorView mAviLoading;
private String TAG = "LoginActivity";
private String mPhoneNum;
private String mPwd;
private ILoginPresenter mILoginPresenter;
@Override
public int getLayoutResId() {
return R.layout.activity_login;
}
@Override
protected void init() {
super.init();
mILoginPresenter = new LoginPresenterImpl(this, LoginActivity.this);
boolean isRemenber = (boolean) SPUtils.get(this, "remember_password", false);
LogUtils.d(TAG, "isRemenber:" + isRemenber);
if (isRemenber) {
//将账号和密码都设置到文本中
String account = (String) SPUtils.get(this, "phoneNum", "");
String password = (String) SPUtils.get(this, "password", "");
mEtPhoneNum.setText(account);
mEtPwd.setText(password);
mCheckBox.setChecked(true);
}
mBtnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mAviLoading.setVisibility(View.VISIBLE);
mPhoneNum = mEtPhoneNum.getText().toString().trim();
mPwd = mEtPwd.getText().toString().trim();
mILoginPresenter.login(mPhoneNum, mPwd);
if (mCheckBox.isChecked()) {
mILoginPresenter.rememberPassword(mPhoneNum, mPwd);
} else {
SPUtils.remove(LoginActivity.this, "remember_password");
SPUtils.remove(LoginActivity.this, "phoneNum");
SPUtils.remove(LoginActivity.this, "password");
}
}
});
}
@Override
public void loginResult(Boolean result, String msg) {
if (result) {
LogUtils.d(TAG, "uid:" + msg);
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("uid", msg);
startActivity(intent);
} else {
CustomToast.show(this, msg + " 请稍后再试!");
}
mAviLoading.setVisibility(View.INVISIBLE);
}
3、总结,MVP结构图:
view层和Presenter层互相持有对方的引用,model只会被presenter层使用。

PS:觉得看了还是不太明白或是好像明白的同学可以自己亲自动手写一写,应该写完就完全可以明白了。
Android MVP模式简单介绍:以一个登陆流程为例的更多相关文章
- android MVP模式简单介绍
原文 http://zhengxiaopeng.com/2015/02/06/Android%E4%B8%AD%E7%9A%84MVP/ 前言 MVP作为一种MVC的演化版本在Android开发中受到 ...
- Android MVP模式简单易懂的介绍方式 (一)
Android MVP模式简单易懂的介绍方式 (一) Android MVP模式简单易懂的介绍方式 (二) Android MVP模式简单易懂的介绍方式 (三) 最近正在研究Android的MVP模式 ...
- Android MVP模式简单易懂的介绍方式 (三)
Android MVP模式简单易懂的介绍方式 (一) Android MVP模式简单易懂的介绍方式 (二) Android MVP模式简单易懂的介绍方式 (三) 讲完M和P,接下来就要讲V了.View ...
- Android MVP模式简单易懂的介绍方式 (二)
Android MVP模式简单易懂的介绍方式 (一) Android MVP模式简单易懂的介绍方式 (二) Android MVP模式简单易懂的介绍方式 (三) 上一篇文章我们介绍完了Model的创建 ...
- Android MVP模式 简单易懂的介绍方式
主要学习这位大神的博客:简而易懂 Android MVP模式 简单易懂的介绍方式 https://segmentfault.com/a/1190000003927200
- Android MVP模式
转自http://segmentfault.com/blogs,转载请注明出处Android MVP Pattern Android MVP模式\[1\]也不是什么新鲜的东西了,我在自己的项目里也普遍 ...
- android MVP模式介绍与实战
android MVP模式介绍与实战 描述 MVP模式是什么?MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数 ...
- MVP 模式简单易懂的介绍方式
为什么用Android MVP 设计模式? 当项目越来越庞大.复杂,参与的研发人员越来越多的时候,MVP 模式 的优势就充分显示出来了. MVP 模式是 MVC 模式在 Android 上的一种变体, ...
- Android MVP模式 谷歌官方代码解读
Google官方MVP Sample代码解读 关于Android程序的构架, 当前(2016.10)最流行的模式即为MVP模式, Google官方提供了Sample代码来展示这种模式的用法. Repo ...
随机推荐
- vuex2 mapActions 报错 `unknown action type: xxxx`
export const setBreadCrumb = ({ dispatch }, data) => { dispatch('SET_BREADCRUMB', data) } 当调用的时候报 ...
- VGG梳理
创新点(小卷积核.小池化核.层数更深.全连接变卷积) 对AlexNet改进,在第一个卷积层中使用了更小的卷积核和卷积stride 多尺度(训练和测试时,采用图片的不同尺度(当然是按各向同性缩放的最小边 ...
- clr调试扩展和DAC
SOS.DLL.SOSEX.DLL这两个就是用来对.NET程序在Windows调试工具中起到翻译作用的调试器扩展.简单讲就是,这两个组件是.NET项目组专门开发出来用来对.NET应用程序进行方便调试用 ...
- 常用方法 DataTable转换为Html
点击单元格 可以输出行和列,这个功能可以在一些特殊的地方用 public static string GetHtmlString(DataTable dt) { StringBuilder sb = ...
- nginx 篇
nginx 安装 下载必要组件 nginx下载地址 http://nginx.org/en/download.html pcre库下载地址,nginx需要 http://sourceforge.net ...
- linux命令之------which命令/cp命令/Head及tail命令/grep命令/pwd命令/cd命令/df命令/mkdir命令/mount及umount命令/ls命令/history命令/ifconfig命令/ping命令/useradd命令/命令passwd/kill命令/su命令/clear命令/ssh命令/tar解压缩/远程拷贝scp
which命令 1) 作用:搜索某个系统命令的位置. 2) 案例:查询vi命令路径:which vi cp命令 1)作用:用于复制文件或目录: 2)-a:此选项通常使用在复制目录时使用,它 ...
- NOI2019 Day2游记
开场T1是个最短路优化建图,边向二维矩形内所有点连,本来可以写树套树的,但是卡空间(128MB),后来发现其实是不用把边都建出来的,只需要用数据结构模拟dijkstra的过程,支持二维区间对一个值取m ...
- map访问key不存在的情况下,用find。比[]直接访问的意思不一样,map[key]不返null
key不存在的话则创建一个pair并调用默认构造函数 map<CGuid, CLibItem>::iterator iterItem = m_world->m_library_sce ...
- ~/.ssh/config文件的使用
Host github-A HostName github.com User git IdentityFile /Users/xxx/.ssh/id_rsa_A IdentitiesOnly yes ...
- bzoj4868 期末考试 题解
https://www.lydsy.com/JudgeOnline/problem.php?id=4868 显然我们只关注最后出分的学科. 刚开始想的是dp,然而不知道如何记录状态. 突然就想到了正解 ...