最近在学习Android的MVP架构,在网上找了许多资料都没有一个清晰的认识,偶然看到简书上骆驼骑士前辈的文章,对MVP的实现过程有了一个较为清晰的认识。后又研究了一下Google官方Demo,分别对两种实现方式做了一个Demo进行对比。

对于Android MVP实现方式,借用一下骆驼骑士前辈的分析

目前常见的MVP在Android里的实践有两种解决方案:
1.直接将Activity看作View,让它只承担View的责任。
2.将Activity看作一个MVP三者以外的一个Controller,只控制生命周期。

下面我们来讲述一下这两种方式的实现过程。


第一种:Activity承担MVP中V的角色

我们以一个不需要网络请求的登录作为示例进行实现。

在这个MVP架构中,我们需要以下几个文件:

类型 文件名
Model 接口 ILoginModel
View 接口 ILoginView
Presenter 接口 ILoginPresenter
Model 接口实现 ILoginModelImpl
View 接口实现 LoginActivity
Presenter 接口实现 ILoginPresenterImpl

首先,我们先定义一个实体类UserEntity,模拟服务器返回的数据。

UserEntity.java
public class UserEntity {/*** 响应码*/private int code;/*** 提示信息*/private String msg;/*** 用户名*/private String user_name;public int getCode() {return code;}public String getMsg() {return msg;}public String getUser_name() {return user_name;}public void setCode(int code) {this.code = code;}public void setMsg(String msg) {this.msg = msg;}public void setUser_name(String user_name) {this.user_name = user_name;}
}

接下来,我们需要编写ILoginModelILoginViewILoginPresenter三个接口

ILoginModel.java
import com.example.mvp.login.CallBack;public interface ILoginModel {/*** 登录相关的业务逻辑在此处理** @param username 账号* @param password 密码* @param callBack 回调接口*/void login(String username, String password, CallBack callBack);
}
ILoginView.java
import com.example.mvp.login.entity.UserEntity;public interface ILoginView {/*** 登录成功的回调显示** @param userEntity 实体类*/void showLoginSuccessMsg(UserEntity userEntity);/*** 登录失败的回调显示** @param errorMsg 失败信息*/void showLoginFailMsg(String errorMsg);
}
ILoginPresenter.java
public interface ILoginPresenter {/*** 连接 Model 和 View 的登录方法** @param username 账号* @param password 密码*/void login(String username, String password);
}

因为在Model中用到了一个回调接口CallBack,所以我们接下来定义一下这个接口:

CallBack.java
import com.example.mvp.login.entity.UserEntity;public interface CallBack {/*** 登录成功的回调** @param userEntity 实体类*/void onSuccess(UserEntity userEntity);/*** 登录失败的回调** @param errorMsg 错误信息*/void onFail(String errorMsg);
}

至此,MVP架构的准备工作都做好了,接下来我们开始实现这些接口,将各个接口连接起来,实现一个登录功能。

大家知道,Model是承担了数据处理和业务逻辑的功能,所以我们来实现一下ILoginModel这个接口。在这个类中,我们假设当账号为 user,密码为 123456 时即登录成功。

LoginModelImpl.java
import com.example.mvp.login.CallBack;
import com.example.mvp.login.entity.UserEntity;public class LoginModelImpl implements ILoginModel {@Overridepublic void login(String username, String password, CallBack callBack) {if (username.equals("user") && password.equals("123456")) {// 模拟服务器返回的数据UserEntity userEntity = new UserEntity();userEntity.setCode(1);userEntity.setMsg("登录成功");userEntity.setUser_name("Vip User");callBack.onSuccess(userEntity);} else {callBack.onFail("用户名或密码错误");}}
}

接下来我们实现一下作为中间联系人的ILoginPresenter接口:

LoginPresenterImpl.java
import com.example.mvp.login.CallBack;
import com.example.mvp.login.entity.UserEntity;
import com.example.mvp.login.model.ILoginModel;
import com.example.mvp.login.view.ILoginView;public class LoginPresenterImpl implements ILoginPresenter {/*** View*/private ILoginView view;/*** Model*/private ILoginModel model;/*** 构造方法** @param view  实现接口的类* @param model 实现接口的类*/public LoginPresenterImpl(ILoginView view, ILoginModel model) {this.view = view;this.model = model;}@Overridepublic void login(String username, String password) {//主动调用model中的login方法实现登录model.login(username, password, new CallBack() {@Overridepublic void onSuccess(UserEntity userEntity) {//如果登录成功,将登录成功的数据传递给View进行显示view.showLoginSuccessMsg(userEntity);}@Overridepublic void onFail(String errorMsg) {//如果登录失败,将登陆失败的数据传递给View显示view.showLoginFailMsg(errorMsg);}});}
}

最后一步,新建一个LoginActivity,实现ILoginView,该activity的内容非常简单,只有两个EditText用于输入账号和密码,一个Button用于点击登录,在这里,只贴出Activity的代码:

LoginActivity.java
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;import com.example.mvp.R;
import com.example.mvp.login.Presenter.ILoginPresenter;
import com.example.mvp.login.Presenter.LoginPresenterImpl;
import com.example.mvp.login.entity.UserEntity;
import com.example.mvp.login.model.LoginModelImpl;public class LoginActivity extends AppCompatActivity implements ILoginView {/*** Presenter*/private ILoginPresenter presenter;/*** 登录按钮*/private Button btn;/*** 用户名输入框*/private EditText etName;/*** 密码输入框*/private EditText etPwd;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_login);//实例化Presenterpresenter = new LoginPresenterImpl(this, new LoginModelImpl());//绑定控件btn = findViewById(R.id.btn);etName = findViewById(R.id.et_name);etPwd = findViewById(R.id.et_pwd);//实现登录的点击事件btn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {String username = etName.getText().toString();String password = etPwd.getText().toString();//检测账号密码均不为空时调用presenter的登录方法if ("".equals(username) || "".equals(password)) {Toast.makeText(LoginActivity.this, "用户名或密码不能为空", Toast.LENGTH_SHORT).show();} else {presenter.login(username, password);}}});}@Overridepublic void showLoginSuccessMsg(UserEntity userEntity) {Toast.makeText(this, userEntity.getUser_name() + "登录成功", Toast.LENGTH_SHORT).show();}@Overridepublic void showLoginFailMsg(String errorMsg) {Toast.makeText(this, errorMsg, Toast.LENGTH_SHORT).show();}
}

以上就是本例的全部代码,现在我们来整理一下整个创建流程:

  1. 创建一个实体类UserEntity,用于模拟服务器返回的数据;
  2. 创建ILoginModelILoginViewILoginPresenter接口;
  3. 创建一个CallBack接口,用于登录回调;
  4. 实现M、P、V接口,创建LoginModelImplLoginPresenterImplLoginActivity,实现登录功能。

实现的效果如下:

我们再来整理一下整个登录过程是如何实现的:

  1. 在 LoginActivity 中声明一个 ILoginPresenter,初始化为 LoginPresenterImpl,向构造函数中传入两个参数—— LoginActivity、LoginModelImpl。
  presenter = new LoginPresenterImpl(this, new LoginModelImpl());
  1. 点击 button,调用 presenter.login() —— 实际上调用的是 LoginPresenterImpl.login()
 presenter.login(username, password);
  1. 在 LoginPresenterImpl.login() 中调用 model.login(),实现 CallBack 接口,将 CallBack 回调的数据推送给 View 进行显示 —— 实际上调用的是 LoginModelImpl.login()
model.login(username, password, new CallBack() {@Overridepublic void onSuccess(UserEntity userEntity) {//如果登录成功,将登录成功的数据传递给View进行显示view.showLoginSuccessMsg(userEntity);}@Overridepublic void onFail(String errorMsg) {//如果登录失败,将登陆失败的数据传递给View显示view.showLoginFailMsg(errorMsg);}});
  1. 在 model.login() 中实现登录判断,将登录结果通过 CallBack 接口提交给 Presenter;
if (username.equals("user") && password.equals("123456")) {// 模拟服务器返回的数据UserEntity userEntity = new UserEntity();userEntity.setCode(1);userEntity.setMsg("登录成功");userEntity.setUser_name("Vip User");callBack.onSuccess(userEntity);
} else {callBack.onFail("用户名或密码错误");
}
  1. 在 LoginActivity 实现的 onLoginSuccessMsg() 、onLoginFailMsg() 中处理登陆成功和失败的提示。
@Override
public void showLoginSuccessMsg(UserEntity userEntity) {Toast.makeText(this, userEntity.getUser_name() + "登录成功", Toast.LENGTH_SHORT).show();
}@Override
public void showLoginFailMsg(String errorMsg) {Toast.makeText(this, errorMsg, Toast.LENGTH_SHORT).show();
}

第二种:将Activity看作一个MVP三者以外的一个Controller,只控制生命周期

我们仍旧以一个不需要网络请求的登录作为示例进行实现。

在这个个MVP架构中,我们需要以下几个文件:

类型 文件名
Model 接口 ILoginModel
View 接口 ILoginView
Presenter 接口 ILoginPresenter
Model 接口实现 ILoginModelImpl
View 接口实现 LoginFragment
Presenter 接口实现 ILoginPresenterImpl
Controller LoginActivity

大家可以看到,和上一个例子相比,我们多了一个 LoginFragment,并且对 View 接口的实现并不是 LoginActivity 来实现了,而是改为LoginFragment。( 其实在Google官方Demo里,View 和 Presenter 的接口是放在一个名为 Contract 的接口文件里的,此处为了更好的理解,将 View 和 Presenter 分别放置在一个文件里。)

因为这个Demo和上一个Demo并没有太大的区别,所以此处只贴出不同部分的代码片段。

LoginModelImpl.java (增加如下代码)
/*** 生成实例*/
private static LoginModelImpl instance = null;public static LoginModelImpl getInstance() {if (instance == null) {instance = new LoginModelImpl();}return instance;
}private LoginModelImpl() {  }
LoginPresenterImpl.java(修改变量和构造方法)
/*** View*/
private ILoginView view;
/*** Model*/
private LoginModelImpl model;
/*** 构造方法** @param view  实现接口的类* @param model 实现接口的类*/
public LoginPresenterImpl(ILoginView view, LoginModelImpl model) {this.view = view;view.setPresenter(this);this.model = model;
}
ILoginView.java(增加如下方法)
/*** 设置Presenter** @param presenter*/
void setPresenter(ILoginPresenter presenter);

下面贴出 LoginActivityLoginFragment 的代码供大家进行比较:

LoginActivity.java
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;import com.example.mvp.R;
import com.example.mvp.login.Presenter.LoginPresenterImpl;
import com.example.mvp.login.model.LoginModelImpl;public class LoginActivity extends AppCompatActivity {/*** Presenter*/LoginPresenterImpl presenter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_login);// 绑定FragmentFragmentManager fragmentManager = getSupportFragmentManager();LoginFragment loginFragment = (LoginFragment) fragmentManager.findFragmentById(R.id.frame_layout);if (loginFragment == null) {loginFragment = LoginFragment.newInstance();if (fragmentManager != null && loginFragment != null) {FragmentTransaction transaction = fragmentManager.beginTransaction();transaction.add(R.id.frame_layout, loginFragment);transaction.commit();}}//实例化Presenterpresenter = new LoginPresenterImpl(loginFragment, LoginModelImpl.getInstance());}
}
LoginFragment.java
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;import com.example.mvp.R;
import com.example.mvp.login.Presenter.ILoginPresenter;
import com.example.mvp.login.entity.UserEntity;public class LoginFragment extends Fragment implements ILoginView {/*** presenter*/private ILoginPresenter presenter;/*** 登录按钮*/private Button btn;/*** 用户名输入框*/private EditText etName;/*** 密码输入框*/private EditText etPwd;/*** 生成实例** @return loginFragment*/public static LoginFragment newInstance() {return new LoginFragment();}public LoginFragment() {}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {View view = inflater.inflate(R.layout.fragment_login, container, false);//绑定控件btn = view.findViewById(R.id.btn);etName = view.findViewById(R.id.et_name);etPwd = view.findViewById(R.id.et_pwd);//实现登录的点击事件btn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {String username = etName.getText().toString();String password = etPwd.getText().toString();//检测账号密码均不为空时调用presenter的登录方法if ("".equals(username) || "".equals(password)) {Toast.makeText(getContext(), "用户名或密码不能为空", Toast.LENGTH_SHORT).show();} else {presenter.login(username, password);}}});return view;}@Overridepublic void setPresenter(ILoginPresenter presenter) {this.presenter = presenter;}@Overridepublic void showLoginSuccessMsg(UserEntity userEntity) {Toast.makeText(getContext(), userEntity.getUser_name() + "登录成功", Toast.LENGTH_SHORT).show();}@Overridepublic void showLoginFailMsg(String errorMsg) {Toast.makeText(getContext(), errorMsg, Toast.LENGTH_SHORT).show();}
}

在本例中,大家可以看到,真正的 View 角色是由 Fragment 承担的,View 相关的操作都在 Fragment 中进行,Activity 不再承担View的角色,而是独立于MVP之外,成为一个单独的个体:

每一个Activity都拥有一个Fragment来作为View,然后每个Activity也对应一个Presenter,在Activity里只处理与生命周期有关的内容,并跳出MVP之外,负责实例化Model,View,Presenter,并负责将三者合理的建立联系,承担的就是一个上帝视角

上述两种模式,一种是实际操作上的简便,将Activity看做View,直接在Activity中实例化Presenter和Model;另一种是建立一个规范且正确的依赖关系,让Activity跳出MVP,负责建立M、V、P 三者的联系,相应的,操作上也会相对复杂,每个Activity都必须有一个独立的Fragment。两种模式,大家根据自己的需要进行取舍即可。

以上呢,即是本人的一些浅薄的见解,如有不全之处,欢迎大家指正。

Android MVP架构实现相关推荐

  1. [Android] Android MVP 架构下 最简单的 代码实现

    Android  MVP 架构下  最简单的 代码实现 首先看图: 上图是MVP,下图是MVC MVP和MVC的区别,在于以前的View层不仅要和model层交互,还要和controller层交互.而 ...

  2. Android MVP架构从入门到精通-真枪实弹

    Android MVP架构从入门到精通-真枪实弹 一. 前言 二. MVC架构 1. MVC架构优缺点 A. 缺点 B. 优点 三. MVP架构 1. MVP架构优缺点 A. 缺点 B. 优点 四. ...

  3. 谈谈 Android MVP 架构 | 掘金技术征文

    前言:本文所写的是博主的个人见解,如有错误或者不恰当之处,欢迎私信博主,加以改正!原文链接,demo链接 MVP 架构简介 说起 MVP 架构,相信很多朋友都看过,网上也有很多这方面的资料.博主使用 ...

  4. android mvp框架基类,Android MVP架构项目搭建封装,基类封装

    综述 对于MVP (Model View Presenter)架构是从著名的MVC(Model View Controller)架构演变而来的.而对于Android应用的开发中本身可视为一种MVC架构 ...

  5. Android MVP架构模式

    目录 概述 实例 Model View Presenter 后记 全部代码 Model View Presenter 概述 MVP模式是Android常见的的一种架构模式,全称是Model.View. ...

  6. Android MVP架构

    Android 架构的理解昨天看了Android框架模式(1)-MVP入门,感觉理解起来有点困难,我就重新把java回调重新理解了一遍,并写了一篇博客,有需要的朋友可以看一看,java回调机制,下面我 ...

  7. 适用于小型项目的 Android MVP 架构

    MVP 架构介绍 其实没什么好介绍的了,网上有很多相关的文章,我就不去 copy 了,反正就是 Google 公司推出的一个适合中大型 Android 项目开发的架构. 之前做了一个项目使用的就是 M ...

  8. android 服务架构,Android MVP架构搭建

    目录 引言 为什么用MVP架构 MVP理论知识 乞丐版MVP架构模式的代码实现 MVP中的代码复用场景 平民版MVP架构 - base层顶级父类 Fragment怎么办 时尚版MVP架构 - Mode ...

  9. android MVP架构分享

    闲来无事,写了一套MVP代码架构,希望能给各位分享一些程序逻辑: 一.java版架构 https://github.com/VcStrong/RxRetrofitMVPDemo.git mvp-v1和 ...

  10. Android —MVP架构—登录页面示例

    1.MVP的诞生 以下内容都是从安卓的角度分析: 首先要了解什么是MVC架构: 图片来源网络 View:对应XML文件及Activity或fragment,因为许多修改视图的操作在Activity中实 ...

最新文章

  1. 卸载破解的Navicat!操作所有的数据库靠它就够了!
  2. 科学家发现跨越生命的重要门槛或许没那么难
  3. Oracle 11g Data Guard 使用duplicate from active database 创建 standby database
  4. APP时代,市场选择是个技术活
  5. Asp.net 后台添加CSS、JS、Meta标签
  6. mysql表内增加一个字段并赋值
  7. 南核目录2020pdf_北核+南核|《消费经济》2020年重点选题
  8. [Jarvis OJ - PWN]——[XMAN]level3
  9. 使用POI导入和导出 Excel文件
  10. MyBatis框架 注解
  11. c++ 字符串拼接_python字符串零碎总结
  12. 企业全面运营管理沙盘模拟心得_企业经营沙盘模拟心得体会
  13. 模拟CMOS集成电路设计基础 第一章 第二章开头
  14. 教你六步拆解 DDD领域驱动设计落地实践
  15. ERC20接口下USDT代币的深入解析
  16. 关于程序集成线上支付模块
  17. 人民日报申论万能结构
  18. 微信开发-NATAPP的使用
  19. docker-compose 部署 php + nginx + mysql + redis
  20. 计算机怎么不休眠,怎么设置电脑不休眠?

热门文章

  1. 不小心中了makop勒索病毒
  2. Excel基础知识(2):如何让单元格出现下拉箭头,以供选择?
  3. 使用adb shell screencap命令截图
  4. 尝试Python的websockets库的最基础功能
  5. 直播svga礼物应该如何设计,让用户更加青睐
  6. EVMC6678L时钟主频配置
  7. AV1:比HEVC/H.265更有效率的视频编码格式?
  8. ios支付 选择货币_iOS In-App Purchase中涉及到的货币单位
  9. **容易混淆的4中park变换**(转载)
  10. 数据链路层LLDP协议