浅谈安卓MVP模式

article/2025/9/23 3:18:58

本篇博文通过对google官方demo:https://github.com/googlesamples/android-architecture/tree/todo-mvp/的理解,用自己的demo更好的讲解mvp的概念,帮助大家如何针对一个Activity页面去编写针对MVP风格的代码。

一、MVP模式介绍

随着UI创建技术的功能日益增强,UI层也履行着越来越多的职责。为了更好地细分视图(View)与模型(Model)的功能,让View专注于处理数据的可视化以及与用户的交互,同时让Model只关系数据的处理,基于MVC概念的MVP(Model-View-Presenter)模式应运而生。

在MVP模式里通常包含4个要素:

(1) View :负责绘制UI元素、与用户进行交互(在Android中体现为Activity);

(2) View interface :需要View实现的接口,View通过View interface与Presenter进行交互,降低耦合,方便进行单元测试;

(3) Model :负责存储、检索、操纵数据(有时也实现一个Model interface用来降低耦合);

(4) Presenter :作为View与Model交互的中间纽带,处理与用户交互的负责逻辑。

这里写图片描述


二、为什么使用MVP模式

在Android开发中,Activity并不是一个标准的MVC模式中的Controller, 它的首要职责是加载应用的布局和初始化用户界面,并接受并处理来自用户的操作请求,进而作出响应。随着界面及其逻辑的复杂度不断提升,Activity类的职责不断增加,以致变得庞大臃肿。当我们将其中复杂的逻辑处理移至另外的一个类(Presenter)中时,Activity其实就是MVP模式中View,它负责UI元素的初始化,建立UI元素与Presenter的关联(Listener之类),同时自己也会处理一些简单的逻辑(复杂的逻辑交由Presenter处理).

另外,回想一下你在开发Android应用时是如何对代码逻辑进行单元测试的?是否每次都要将应用部署到Android模拟器或真机上,然后通过模拟用户操作进行测试?然而由于Android平台的特性,每次部署都耗费了大量的时间,这直接导致开发效率的降低。而在MVP模式中,处理复杂逻辑的Presenter是通过interface与View(Activity)进行交互的,这说明了什么?说明我们可以通过自定义类实现这个interface来模拟Activity的行为对Presenter进行单元测试,省去了大量的部署及测试的时间。


三、MVP与MVC的异同

MVC模式与MVP模式都作为用来分离UI层与业务层的一种开发模式被应用了很多年。在我们选择一种开发模式时,首先需要了解一下这种模式的利弊:

无论MVC或是MVP模式都不可避免地存在一个弊端:额外的代码复杂度及学习成本。

但比起他们的优点,这点弊端基本可以忽略了:

(1)降低耦合度
(2)模块职责划分明显
(3)利于测试驱动开发
(4)代码复用
(5)隐藏数据
(6)代码灵活性

对于MVP与MVC这两种模式,它们之间也有很大的差异。有一些程序员选择不使用任何一种模式,有一部分原因也许就是不能区分这两种模式差异。以下是这两种模式之间最关键的差异:

MVP模式:

1.View不直接与Model交互 ,而是通过与Presenter交互来与Model间接交互
2.Presenter与View的交互是通过接口来进行的,更有利于添加单元测试
3.通常View与Presenter是一对一的,但复杂的View可能绑定多个Presenter来处理逻辑     

MVC模式:

1.View可以与Model直接交互
2.Controller是基于行为的,并且可以被多个View共享
3.可以负责决定显示哪个View

四、利用MVP进行Android开发的例子

用mvp模式实现:通过输入用户名跟密码,点击登录,登录成功后,清除输入信息,并提示成功。

  1. 运行效果先看下:
    这里写图片描述 这里写图片描述

2.目录结构:
这里写图片描述
可以发现,View不直接与Model交互 ,而是通过与Presenter交互来与Model间接交互并且Presenter与Model、View都是通过接口来进行交互的,既降低耦合也方便进行单元测试。

3.具体实现:
1)首先实体类LoginResponse 不用考虑这个肯定有,其次从效果图可以看到至少有一个业务方法login(),这两点没什么难度,我们首先完成:

/*** <功能详细描述>** @author caoyinfei* @version [版本号, 2016/5/4]* @see [相关类/方法]* @since [产品/模块版本]*/
public class LoginResponse {private String resutlCode;private String resultInfo;public String getResutlCode() {return resutlCode;}public String getResultInfo() {return resultInfo;}public void setResutlCode(String resutlCode) {this.resutlCode = resutlCode;}public void setResultInfo(String resultInfo) {this.resultInfo = resultInfo;}
}

2)模型类(Model层)
模型实现类通常是对本地数据库的操作或者是通过网络请求获取网络数据的操作 。

/*** @Title:* @Description: 模型抽象类* @Author:cyf* @Since:2016/11/23* @Version:*/public interface LoginTaskDataSource {/*** 保存登录信息到本地* @param loginResponse*/void saveLoginResponse(LoginResponse loginResponse);/*** 登录请求* @param loginReq* @param callback*/void login(LoginReq loginReq, NetTasksCallback callback);/*** 网络请求回调*/interface NetTasksCallback {void onSuccess(LoginResponse loginResponse);void onFailure(String errorMsg);}}
/*** model层,处理数据(包括网络,数据库等等)* 主要就是把presenter层拆分,以后网络框架,数据库换框架,不用动业务代码,只需要改Repository层代码*/
public class LoginTasksRepository implements LoginTaskDataSource {@Overridepublic void saveLoginResponse(LoginResponse loginResponse) {}@Overridepublic void login(LoginReq loginReq, NetTasksCallback callback) {//开启网络请求/*** 网络请求,这边暂时省略*///成功后,回调到presenter层中LoginResponse response = new LoginResponse();response.setResultInfo(loginReq.getName() + "====" + loginReq.getPassword() + "====登录成功");response.setResutlCode("0");callback.onSuccess(response);}}

3)再来看看View接口:
上面我们说过,Presenter与View交互是通过接口。所以我们这里需要定义一个IUserLoginView ,难点就在于应该有哪些方法,我们这个是登录页面,其实有哪些功能,就应该有哪些方法,比如登录成功,失败,弹出加载框这些都要通知ui(Activity)去更新。所以定义了如下方法:

/*** <功能详细描述>** @author caoyinfei* @version [版本号, 2016/5/4]* @see [相关类/方法]* @since [产品/模块版本]*/
public interface IMvpView {void onError(String errorMsg, String code);void onSuccess();void showLoading();void hideLoading();
}
public interface IUserLoginView extends IMvpView {void clearEditContent();
}

4)Presenter
Presenter是用作Model和View之间交互的桥梁。

/*** <基础业务类>** @author caoyinfei* @version [版本号, 2016/6/6]* @see [相关类/方法]* @since [V1]*/
public interface Presenter<V> {void attachView(V view);void detachView(V view);String getName();
}
/*** <基础业务类>** @author caoyinfei* @version [版本号, 2016/6/6]* @see [相关类/方法]* @since [V1]*/
public abstract class BasePresenter<V extends IMvpView> implements Presenter<V> {protected V mvpView;public void attachView(V view) {mvpView = view;}@Overridepublic void detachView(V view) {mvpView = null;}@Overridepublic String getName() {return mvpView.getClass().getSimpleName();}
}
/*** <绑定View层和Model层>** @author caoyinfei* @version [版本号, 2016/5/4]* @see [相关类/方法]* @since [产品/模块版本]*/
public class LoginPresenter extends BasePresenter<IUserLoginView> {private LoginTasksRepository loginTaskDataSource;public LoginPresenter() {loginTaskDataSource = new LoginTasksRepository();}public void login(LoginReq loginReq) {loginTaskDataSource.login(loginReq, new LoginTaskDataSource.NetTasksCallback() {@Overridepublic void onSuccess(LoginResponse loginResponse) {mvpView.hideLoading();mvpView.onSuccess(loginResponse);}@Overridepublic void onFailure(String errorMsg) {}});}
}

5)activity实现代码

/*** <登录界面>** @author caoyinfei* @version [版本号, 2016/5/4]* @see [相关类/方法]* @since [产品/模块版本]*/
public class MainActivity extends Activity implements View.OnClickListener, IUserLoginView {/*** 用户名*/private EditText userName;/*** 密码*/private EditText passWord;/*** 登录按钮*/private Button button;private LoginPresenter mUserLoginPresenter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initView();loadData();}/*** 初始化布局组件*/private void initView() {userName = (EditText) findViewById(R.id.user_name);passWord = (EditText) findViewById(R.id.password);button = (Button) findViewById(R.id.login);/*** 添加按钮点击事件*/button.setOnClickListener(this);}/*** 初始化数据*/private void loadData() {mUserLoginPresenter = new LoginPresenter();mUserLoginPresenter.attachView(this);}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.login:LoginReq loginReq = new LoginReq();loginReq.setName(userName.getText().toString());loginReq.setPassword(passWord.getText().toString());mUserLoginPresenter.login(loginReq);break;default:break;}}@Overridepublic void onError(String errorMsg, String code) {}@Overridepublic void onSuccess(Object response) {if (response instanceof LoginResponse) {LoginResponse loginResponse = (LoginResponse) response;Toast.makeText(this, loginResponse.getResultInfo(), 1).show();}}@Overridepublic void showLoading() {}@Overridepublic void hideLoading() {}@Overridepublic void clearEditContent() {userName.setText("");passWord.setText("");}@Overrideprotected void onDestroy() {mUserLoginPresenter.detachView(this);//Presenter对象持有Activity对象,这条GC链不剪断,Activity就无法被完整回收,造成内存泄漏super.onDestroy();}
}

个人理解,如有不同见解,欢迎留言~~
代码下载地址:http://download.csdn.net/detail/dfskhgalshgkajghljgh/9698405


如有错误欢迎指出来,一起学习。
在这里插入图片描述


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

相关文章

简单易懂 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…

MVP框架模式

一、基本概念 MVP是Model-View-Presenter的简称&#xff0c;即模型-视图-表现层的缩写。MVP是由MVC模式进化而来的&#xff0c;MVP改进了MVC中的控制器过于臃肿的问题。 与MVC一样&#xff0c;MVP将应用程序的数据处理、数据显示和逻辑控制分开&#xff0c;用一种业务逻辑、数…

分析几个面试题:==和===;绑定事件;正则表达式

今天也是我学后端的朋友给我发了三个前端的面试题&#xff0c;这里我们试着分析一波。 目录 1、和的含义是什么&#xff0c;又有什么区别呢&#xff1f; &#xff08;1&#xff09;赋值&#xff1a; &#xff08;2&#xff09;相同&#xff1a; &#xff08;3&#xff09;…

前端面经总结

HTML及浏览器 对栅栏布局的理解 栅格化布局中的元素&#xff1a;column列&#xff0c;row行&#xff0c;gutter列之间的距离&#xff0c;container容器 栅格是否可以嵌套 canvas和svg的区别 SVG&#xff1a; SVG 是一种使用 XML 描述 2D 图形的语言。 在 SVG 中&#xff0c;…

前端深入学习

但是面试中必考的点且占比非常大的有前端基础和算法。 决定你是否能拿sp offer&#xff08;高薪offer&#xff09;以及是否进名企的是项目和算法。 absolute 生成绝对定位的元素&#xff0c;相对于 static 定位以外的第一个父元素进行定位。 元素的位置通过 “left”, “to…

web前端常用词汇

html中的单词 Network [netwɜːk] 网络 General [dʒen(ə)r(ə)l] 一般的&#xff0c;大体的 Request [rɪkwest] 请求 Response [rɪspɒns] 响应 Headers [hedəz] 标题 HyperText [haɪpətekst] 超文本 Transfer [trnsfɝ] 传递 Protocol [prəʊtə…

前端html、css、JavaScript---硬核知识汇总

前端HTML篇 硬核&#xff01;一篇文章教你阅遍html。 声明&#xff1a;本篇文章只是一个刚开始学习后端开发的菜鸟汇总完成的 JavaWeb学习前导html篇&#xff0c;所以专业性肯定不如前端人员&#xff0c;但用于学习后端开发足够了&#xff0c;刚接触html的童鞋拿来快速了解ht…

前端面试题总结(转载)

DOM结构 —— 两个节点之间可能存在哪些关系以及如何在节点之间任意移动1.DOM中两个节点存在的关系无非3种&#xff1a;1.1.包含与被包含,IE率先引入的contains()方法可检测&#xff0c;例 A.contains(B)&#xff0c;即检查节点B是否是节点A的子节点&#xff0c;返回布尔值&…

前端三件套知识点

html 常用插件 文件比较 常用快捷键 ctrl f 查找当前页面的内容 !后按table 或者 html:5 代码模板快捷键 altb调试快捷键 ctrlk ctrlt改颜色主题 altshifta注释快捷键 html基础概念 一、 WEB标准的概念及组成 WEB标准不是某一个标准&#xff0c;而是一系列标准的集合。 …

php前端搜索功能,JavaScript实现前端实时搜索功能的代码分享(图)

这篇文章主要为大家详细介绍了JavaScript实现前端实时搜索功能&#xff0c;具有一定的参考价值&#xff0c;感兴趣的小伙伴们可以参考一下 大部分页面都具备搜索功能。而作为前端&#xff0c;我们的目的只是将用户输入的内容返回给后台而后呈现反馈数据给用户&#xff0c;具体实…

前端实时搜索功能

** 大部分页面都具备搜索功能。而作为前端&#xff0c;我们的目的只是将用户输入的内容返回给后台而后呈现反馈数据给用户&#xff0c;具体实现如下&#xff1a; ** 1.基本布局&#xff1a; <div class"searcher"><p class"searcher-main">…

适合后端开发人员的html笔记

1html 锚点 <a href"#bottom">跳转到底部</a> <div class"aa"></div> <a href"#" id"bottom">跳转到顶部</a> img 图片标签 h1:一级标题块级元素其他的都是行内元素 img同时具备行内元素和块级…

【JavaScript】 一万字 JavaScript 笔记(详细讲解 + 代码演示 + 图解)

文章目录 变量 变量声明的提升数据类型Typeof运算符数据类型转换 表达式与运算符 隐式类型转换toFixed(a)方法保留a位小数关系运算符短路求值流程控制语句 数组函数DOMBOM面向对象 变量 一个变量只定义但没有赋初值&#xff0c;默认值是 undefined定义变量时必须写var&a…

前端面试 汇总整理

-----------------------------------------html css-------------------------------------------- 一.回流与重绘 回流一定引起重绘&#xff0c;重绘不一定回流 1.浏览器渲染机制 1&#xff09;浏览器采用流式布局。 2&#xff09;浏览器将html解析成DOM&#xff0c;css解析成…

JavaWeb——后端开发必备的JavaScript知识

JS学习 1.什么是JavaScript&#xff0c;有什么用&#xff1f; 答&#xff1a;JavaScript是网景公司发明的&#xff0c;运行在浏览器上的脚本语言&#xff0c;简称JS。 补充&#xff1a;网景公司现在没了&#xff0c;被美国在线收购了。 2.在HTML中嵌入JavaScript代码的三种方…

el-input中设置onkeypress事件是否匹配正则表达式显示输入内容的格式

场景 若依前后端分离版手把手教你本地搭建环境并运行项目&#xff1a; 若依前后端分离版手把手教你本地搭建环境并运行项目_BADAO_LIUMANG_QIZHI的博客-CSDN博客_若依前后端分离搭建 设置el-input的onkeypress事件限制输入内容是否匹配正则表达式。 注&#xff1a; 博客&a…

前端开发实习生面试总结

个人感觉面试官对实习生还是很友好的&#xff0c;大部分时候你答不上来都会引导你&#xff0c;最后还会点评你的不足&#xff0c;评价出来的那些问题确实是我自己的短板&#xff01;所以在整个过程中还是学到了很多&#xff01;&#xff01; 本人坐标南京&#xff0c;南京投的相…

datacenter 2

突然综合类又有新的事情了 页面能顺利打开了 但是好像值传不过去 之前不知道为什么输出了三十次 之前是判断两个不为4和200 然后else输出 现在改了之后只输入10次 但是出现通讯错误那个问题