Android 架构 MVC MVP MVVM,这一波你应该了然于心
MVC,MVP和MVVM是软件比较常用的三种软件架构,这三种架构的目的都是分离,避免将过多的逻辑全部堆积在一个类中。
在Android中,Activity中既有UI的相关处理逻辑,又有数据获取逻辑,从而导致Activity逻辑复杂不单一难以维护。
为了一个应用可以更好的维护和扩展,我们需要很好的区分相关层级,要不然以后将数据获取方式从数据库变为网络获取时,我们需要去修改整个Activity。架构使得View和数据相互独立,我们把应用分成三个不同层级,这样我们就能够单独测试相关层级,使用架构能够把大多数逻辑从Activity中移除,方便进行单元测试。
MVC是什么?
MVC是模型(Model)-视图(View)-控制器(Controller)的缩写,用一种业务逻辑、数据、界面显示分离的方法组织代码。其实Android Studio创建一个项目的模式就是一个简化的mvc模式。
Android中的MVC含义
Model:实体类(数据的获取、存储、数据状态变化)。
View:布局文件
Controller:Activity(处理数据、业务和UI)。
工作原理

View接受用户的交互请求。
View将请求转交给Controller。
Controller操作Model进行数据更新。
数据更新之后,Model通知View数据变化。
View显示更新之后的数据。
MVC的缺点
随着界面及其逻辑的复杂度不断提升,Activity类的职责不断增加,以致变得庞大臃肿。
为了解决MVC的缺点,MVP 框架被提出来。
MVP是什么
MVP是MVC架构的一个演化版,全称是Model-View-Presenter。将MVC中的V和C结合生成MVP中的V,引入新的伙伴Presenter。
Android中的MVP含义
Model:实体类(数据的获取、存储、数据状态变化)。
View:布局文件+Activity。
Presenter:中介,负责完成View与Model间的交互和业务逻辑。
工作原理

View 接收用户交互请求
View 将请求转交给 Presenter(V调用P接口)
Presenter 操作Model进行数据更新(P调用M接口)
Model 通知Presenter数据发生变化(M调用P接口)
Presenter 更新View数据(P执行接口,V相应回调)
MVP的优点
复杂的逻辑处理放在Presenter进行处理,减少了Activity的臃肿。
解耦。Model层与View层完全分离,修改V层不会影响M层,降低了耦合性。
可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑。
Presenter层与View层的交互是通过接口来进行的,便于单元测试。
MVP的缺点
维护困难。Presenter中除了业务逻辑以外,还有大量的View->Model,Model->View的手动同步逻辑,造成Presenter比较笨重,维护起来会比较困难。
MVVM是什么
是 Model-View-ViewModel 的简写。MVVM与MVP的结构还是很相似的,就是将Presenter升级为ViewModel。在MVVM中,View层和Model层进行了双向绑定(即Data Binding),所以Model数据的更改会表现在View上,反之亦然。ViewModel就是用来根据具体情况处理View或Model的变化。
Android中的MVVM含义
Model:实体类(数据的获取、存储、数据状态变化)。
View:布局文件+Activity。
ViewModel: 关联层,将Model和View进行绑定,Model或View更改时,实时刷新对方。
工作原理

View 接收用户交互请求
View 将请求转交给ViewModel
ViewModel 操作Model数据更新
Model 更新完数据,通知ViewModel数据发生变化
ViewModel 更新View数据
View/Model的变动,只要改其中一方,另一方都能够及时更新到
MVVM的优点
1.提高可维护性。Data Binding可以实现双向的交互,使得视图和控制层之间的耦合程度进一步降低,分离更为彻底,同时减轻了Activity的压力。
2.简化测试。因为同步逻辑是交由Binder做的,View跟着Model同时变更,所以只需要保证Model的正确性,View就正确。大大减少了对View同步更新的测试。
3.ViewModle易于单元测试。
MVVM的缺点
1.对于简单的项目,使用MVVM有点大材小用。
2.对于过大的项目,数据绑定会导致内存开销大,影响性能。
3.ViewModel和View的绑定,使页面异常追踪变得不方便。有可能是View出错,也有可能是ViewModel的业务逻辑有问题,也有可能是Model的数据出错。
MVP和MVC的最大区别
在MVP中View并不直接使用Model,它们之间的通信是通过Presenter 来进行的,所有的交互都发生在Presenter内部,而在MVC中View直接从Model中读取数据而不是通过 Controller。
如何选取框架
本来是要每个模式写一个适用场景,最后想想每个人都有自己的理解,别被他人束缚了。
一句话:适合自己的才是最好的!
实例

就这么一个界面咱通过MVC、MVP、MVVM分别搭建一下。
MVC实例
代码结构

1.在layout创建一个布局文件
<!--缩减版--><LinearLayout...><EditTextandroid:id="@+id/et_account".../></LinearLayout><LinearLayout...><EditTextandroid:id="@+id/et_password".../></LinearLayout><Buttonandroid:id="@+id/btn_login".../><Buttonandroid:id="@+id/btn_back".../>2.实体类(User)
public class User {private String name;private String password;public User() {}//set or get ...public User(String name, String password) {this.name = name;this.password = password;}
}3.MVCLoginActivity
//用户点击事件
mvcBinding.mcvLogin.btnLogin.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {user.setName(mvcBinding.mcvLogin.etAccount.getText().toString());user.setPassword(mvcBinding.mcvLogin.etPassword.getText().toString());login(user);}
});
//逻辑处理
private void login(User user){if(!user.getName().isEmpty()&&!user.getPassword().isEmpty()){if(user.getName().equals("scc001")&&user.getPassword().equals("111111")){Toast.makeText(this,"登录成功",Toast.LENGTH_SHORT).show();}else{Toast.makeText(this,"登录失败",Toast.LENGTH_SHORT).show();}}else {Toast.makeText(this,"登录失败",Toast.LENGTH_SHORT).show();}}MVP实例
代码结构

1.Model层
实体类bean,同MVC中的User类,就不贴代码浪费大家时间了。
Model层所要执行的业务逻辑
/*** 功能:接口,表示Model层所要执行的业务逻辑*/
public interface LoginModel {//User实体类;OnLoginFinishedListener presenter业务逻辑的返回结果void login(User user, OnLoginFinishedListener listener);
}实现类(实现LoginModel接口)
/*** 功能:实现Model层逻辑*/
public class LoginModelImpl implements LoginModel {//第4步:验证帐号密码@Overridepublic void login(User user, OnLoginFinishedListener listener) {if(user.getName().isEmpty()||!user.getName().equals("scc001")){//第5步:Model层里面回调Presenter层listenerlistener.onUserNameError();}else if(user.getPassword().isEmpty()||!user.getPassword().equals("111111")){//第5步:Model层里面回调Presenter层listenerlistener.onPasswordError();}else {//第5步:Model层里面回调Presenter层listenerlistener.onSuccess();}}
}2.Presenter层
当Model层得到请求的结果,回调Presenter层,让Presenter层调用View层的接口方法。
/*** 功能:当Model层得到请求的结果,回调Presenter层,让Presenter层调用View层的接口方法。*/
public interface OnLoginFinishedListener {void onUserNameError();void onPasswordError();void onSuccess();
}完成登录的验证,以及销毁当前View。
/*** 功能:登录的Presenter的接口,实现类为LoginPresenterImpl,* 完成登录的验证,以及销毁当前View。*/
public interface LoginPresenter {//完成登录的验证void verifyData(User user);//销毁当前Viewvoid onDestroy();
}Presenter实现类,引入 LoginModel(model)和LoginView(view)的引用
/*** 功能:实现类,引入 LoginModel(model)和LoginView(view)的引用*/
public class LoginPresenterImpl implements OnLoginFinishedListener, LoginPresenter {//View层接口private LoginView loginView;//Model层接口private LoginModel loginModel;public LoginPresenterImpl(LoginView loginView) {this.loginView = loginView;this.loginModel = new LoginModelImpl();}//第6步:通过OnLoginFinishedListener验证结果回传到Presenter层@Overridepublic void onUserNameError() {if (loginView != null) {//第7步:通过loginView回传到View层loginView.setUserNameError();loginView.hideProgress();}}//第6步:通过OnLoginFinishedListener验证结果回传到Presenter层@Overridepublic void onPasswordError() {if (loginView != null) {//第7步:通过loginView回传到View层loginView.setPasswordError();loginView.hideProgress();}}//第6步:通过OnLoginFinishedListener验证结果回传到Presenter层@Overridepublic void onSuccess() {if (loginView != null) {//第7步:通过loginView回传到View层loginView.success();loginView.hideProgress();}}@Overridepublic void verifyData(User user) {if (loginView != null) {loginView.showProgress();}//第3步:调用model层LoginModel接口的login()方法loginModel.login(user,this);}@Overridepublic void onDestroy() {loginView = null;}
}3.View层
布局文件同MVC中的View层,就不贴代码浪费大家时间了。
Presenter与View交互是通过接口。
/*** 功能:Presenter与View交互是通过接口。* 接口中方法的定义是根据Activity用户交互需要展示的控件确定的。*/
public interface LoginView {//login是个耗时操作,加载中(一般用ProgressBar)void showProgress();//加载完成void hideProgress();//login账号失败给出提示void setUserNameError();//login密码失败给出提示void setPasswordError();//login成功void success();
}MVPLoginActivity
/*** 功能:需要实现LoginView接口。*/
public class MVPLoginActivity extends AppCompatActivity implements LoginView {LoginPresenterImpl loginPresenterImpl;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {...//创建一个Presenter对象loginPresenterImpl = new LoginPresenterImpl(MVPLoginActivity.this);//第1步:用户点击登录mvpBinding.mvpLogin.btnLogin.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {User user = new User();user.setName(mvpBinding.mvpLogin.etAccount.getText().toString());user.setPassword(mvpBinding.mvpLogin.etPassword.getText().toString());//第2步:调用Presenter接口中的验证方法loginPresenterImpl.verifyData(user);}});}@Overridepublic void showProgress() {//加载中}@Overridepublic void hideProgress() {//加载完成}@Overridepublic void setUserNameError() {//第7步:通过loginView回传到View层//账号错误Toast.makeText(this,"登录失败",Toast.LENGTH_SHORT).show();}@Overridepublic void setPasswordError() {//第7步:通过loginView回传到View层//密码错误Toast.makeText(this,"登录失败",Toast.LENGTH_SHORT).show();}@Overridepublic void success() {//第7步:通过loginView回传到View层Toast.makeText(this,"登录成功",Toast.LENGTH_SHORT).show();//登录成功}@Overrideprotected void onDestroy() {super.onDestroy();loginPresenterImpl.onDestroy();}
}MVVM实例
1.Model层
实体类bean,同MVC中的User类,就不贴代码浪费大家时间了。
2.ViewModel层
ViewModel类,继承自ViewModel
public class LoginViewModel extends ViewModel {public ViewDataBinding binding;public LoginViewModel(ViewDataBinding binding){this.binding = binding;}public void getUser(String userName, String password, Callback callback) {//逻辑处理User user = new User();user.setPassword("111111");if(userName.isEmpty()||!userName.equals("scc001")){user.setName("scc005");}else if(password.isEmpty()||!password.equals("111111")){user.setName("scc004");}else {user.setName("scc003");}callback.onCallBack(user);}
}
ViewModel与View交互/*** 功能:ViewModel与View交互。*/
public interface Callback<T> {void onCallBack(T t);
}3.View层
先看布局文件,布局文件使用了DataBinding。
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"><data><!--为引入的类从新起一个变量名,方便下面使用--><variablename="user"type="com.scc.architecture.mvvm.model.User" /></data><!--删减版--><LinearLayout...><LinearLayout...><EditTextandroid:id="@+id/et_account"...android:text="@={user.name}" /></LinearLayout><LinearLayout...><EditTextandroid:id="@+id/et_password"...android:text="@={user.password}" /></LinearLayout><Buttonandroid:id="@+id/btn_login".../></LinearLayout>
</layout>本来Button点击事件也想用databinding去做,后来觉得这个是MVP模式就忽略了这个知识点,感兴趣的可以自己捣鼓一下,databinding还是挺好玩的。
MVVMLoginActivity
public class MVVMLoginActivity extends AppCompatActivity {private LoginViewModel loginVM;ActivityMvvmBinding mvvmBinding;private EditText et_account,et_password;private Button btn_login,btn_back;private TextView tv_title;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);mvvmBinding = DataBindingUtil.setContentView(this, R.layout.activity_mvvm);et_account =findViewById(R.id.et_account);et_password =findViewById(R.id.et_password);btn_login = findViewById(R.id.btn_login);tv_title = findViewById(R.id.tv_title);tv_title.setText("MVVM");loginVM = new LoginViewModel(mvvmBinding);User user = new User( "scc001", "111111");mvvmBinding.setUser(user);//设置et_account:scc001|et_password:111111//第1步:用户点击登录btn_login.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {login(et_account.getText().toString(),et_password.getText().toString());}});}private void login(String name,String password) {loginVM.getUser(name,password, new Callback<User>() {@Overridepublic void onCallBack(User user) {mvvmBinding.setUser(user);//同步设置控件}});}
}写到这里MVC、MCP、MVVM和实例基本写完了,但是感觉自己理解的不是很好,有大佬能指点就更好了。最后,希望对你有借鉴意义。
Android知识点:
Android开发核心知识点笔记
Android Framework知识点笔记
相关文章:
Android 架构 MVC MVP MVVM,这一波你应该了然于心
MVC,MVP和MVVM是软件比较常用的三种软件架构,这三种架构的目的都是分离,避免将过多的逻辑全部堆积在一个类中。在Android中,Activity中既有UI的相关处理逻辑,又有数据获取逻辑,从而导致Activity逻辑复杂不单…...
物联网在医疗保健领域的5大创新应用
如今,物联网的发展越来越迅速,我们无法低估物联网在当今世界的重要性。大多数人每天都会使用到物联网设备。例如,当你使用智能手表来跟踪你的锻炼时,你就间接地使用了物联网的功能。由于物联网为世界带来了很多有效的帮助…...
【一天一门编程语言】Haskell 语言程序设计极简教程
Haskell 语言程序设计极简教程 一、什么是 Haskell Haskell 是一种纯函数式编程语言,它把程序设计抽象化到一个更高的层次,简化程序开发工作量,能够更快更容易地完成任务。 它是一种函数式编程语言,它采用函数式编程方法&#…...
getStaticPaths函数 以及 fallback参数
getStaticPaths是Next.js的一个静态生成API,它用于在构建时确定哪些页面需要被预渲染。它需要返回一个包含params属性的对象数组,其中每个对象都代表一个路径参数集合,可以被预渲染为一个静态页面。如果所有参数都已知,它们将被硬…...
msys2+minGW方案编译ffmpeg的最佳实践
一、Win10 64bit编译环境的建立1)从http://www.msys2.org/下载 msys2-x86_64-xxx.exe2) 安装msys2到默认路径 C:\msys64\3) 运行MSYS2 w644)执行 pacman -Syu 更新系统当出现提示时,选择y5) 当窗口关闭时,重…...
理解redis的数据结构
redis为什么快? 首先可以想到内存读写数据本来就快,然后IO复用快,单线程没有静态消耗和锁机制快。 还有就是数据结构的设计快。这是因为,键值对是按一定的数据结构来组织的,操作键值对最终就是对数据结构进行增删改查操…...
Lecture6 逻辑斯蒂回归(Logistic Regression)
目录 1 常用数据集 1.1 MNIST数据集 1.2 CIFAR-10数据集 2 课堂内容 2.1 回归任务和分类任务的区别 2.2 为什么使用逻辑斯蒂回归 2.3 什么是逻辑斯蒂回归 2.4 Sigmoid函数和饱和函数的概念 2.5 逻辑斯蒂回归模型 2.6 逻辑斯蒂回归损失函数 2.6.1 二分类损失函数 2.…...
File类及IO流说明
目录 1.File类说明 (1)构造方法创建文件 (2)创建功能 (3)File类的判断和获取功能 (4)文件删除功能 2.I/O流说明 (1).分类 3.字节流写数据 (1)说明 (2)字节流写数据的三种方式 (3)写入时实现换行和追加写入 (4)异常处理中加入finally实现资源的释放 4.字节流读数据 …...
优秀的网络安全工程师应该有哪些能力?
网络安全工程师是一个各行各业都需要的职业,工作内容属性决定了它不会只在某一方面专精,需要掌握网络维护、设计、部署、运维、网络安全等技能。目前稍有经验的薪资在10K-30K之间,全国的网络安全工程师还处于一个供不应求的状态,因…...
[C++11] auto初始值类型推导
背景:旧标准的auto 在旧标准中,auto代表“具有自动存储期的 局部变量” auto int i 0; //具有自动存储期的局部变量 //C98/03,可以默认写成int i0; static int j 0; //静态类型的定义方法实际上,我们很少使用auto,…...
【Java】List集合去重的方式
List集合去重的方式方式一:利用TreeSet集合特性排序去重(有序)方式二:利用HashSet的特性去重(无序)方式三:利用LinkedHashSet去重(有序)方式四:迭代器去重&am…...
每个人都应该知道的5个NLP代码库
在本文中,将详细介绍目前常用的Python NLP库。内容译自网络。这些软件包可处理多种NLP任务,例如词性(POS)标注,依存分析,文档分类,主题建模等等。NLP库的基本目标是简化文本预处理。目前有许多工…...
SPI协议介绍
SPI协议介绍 文章目录SPI协议介绍一、 SPI硬件知识1.1 硬件连线1.2 SPI控制器内部结构二、 SPI协议2.1 传输示例2.2 SPI模式致谢一、 SPI硬件知识 1.1 硬件连线 引脚含义如下: 引脚含义DO(MOSI)Master Output, Slave Input,SPI主控用来发出数据&#x…...
MySQL数据库中索引的优点及缺点
一、索引的优点 1)创建索引可以大幅提高系统性能,帮助用户提高查询的速度; 2)通过索引的唯一性,可以保证数据库表中的每一行数据的唯一性; 3)可以加速表与表之间的链接; 4&#…...
(q)sort函数总结(基础篇)
1.sort函数 介绍:这是一个C的函数,包含于algorithm头文件中。 基本格式: sort(起始地址(常为变量名),排序终止的地址(变量名加上排序长度),自定义的比较函数) 重点&a…...
【数据库】MongoDB数据库详解
目录 一,数据库管理系统 1, 什么是数据库 2,什么是数据库管理系统 二, NoSQL 是什么 1,NoSQL 简介 2,NoSQL数据库 3,NoSQL 与 RDBMS 对比 三,MongoDB简介 1, MongoDB 是什…...
【linux】进程间通信——system V
system V一、system V介绍二 、共享内存2.1 共享内存的原理2.2 共享内存接口2.2.1 创建共享内存shmget2.2.2 查看IPC资源2.2.3 共享内存的控制shmctl2.2.4 共享内存的关联shmat2.2.5 共享内存的去关联shmdt2.3 进程间通信2.4 共享内存的特性2.5 共享内存的大小三、消息队列3.1 …...
计算机网络的基本组成
计算机网络是由多个计算机、服务器、网络设备(如路由器、交换机、集线器等)通过各种通信线路(如有线、无线、光纤等)和协议(如TCP/IP、HTTP、FTP等)互相连接组成的复杂系统,它们能够在物理层、数…...
【数据结构趣味多】Map和Set
1.概念及场景 Map和set是一种专门用来进行搜索的容器或者数据结构,其搜索的效率与其具体的实例化子类有关。 在此之前,我还接触过直接查询O(N)和二分查询O(logN),这两个查询有很多不足之出,直接查询的速率太低,而二分查…...
Redis 之企业级解决方案
文章目录一、缓存预热二、缓存雪崩三、缓存击穿四、缓存穿透五、性能指标监控5.1 监控指标5.2 监控方式🍌benchmark🍌monitor🍌slowlog提示:以下是本篇文章正文内容,Redis系列学习将会持续更新 一、缓存预热 1.1 现象…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...
DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...
html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...
Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式
今天是关于AI如何在教学中增强学生的学习体验,我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育,这并非炒作,而是已经发生的巨大变革。教育机构和教育者不能忽视它,试图简单地禁止学生使…...
FFmpeg:Windows系统小白安装及其使用
一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】,注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录(即exe所在文件夹)加入系统变量…...
【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制
目录 节点的功能承载层(GATT/Adv)局限性: 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能,如 Configuration …...
