Android 架构 MVC MVP MVVM,这一波你应该了然于心

news/2024/3/29 14:20:44/文章来源:https://blog.csdn.net/m0_70748845/article/details/129203548

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知识点笔记

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_73799.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Wireshark抓包

Wireshark 1 抓包时间显示格式 2 界面显示列设置 3 protocol协议解析 4 过滤器 tcp.port&#xff1a;TCP端口tcp.dstport&#xff1a;TCP目的端口tcp.srcport&#xff1a;TCP源端口udp.port&#xff1a;UDP端口udp.dstport&#xff1a;UDP目的端口udp.srcport&#xff1a;UDP…

月薪过3W的软件测试工程师,都是怎么做到的?

对任何职业而言&#xff0c;薪资始终都会是众多追求的重要部分。前几年的软件测试行业还是一个风口&#xff0c;随着不断地转行人员以及毕业的大学生疯狂地涌入软件测试行业&#xff0c;目前软件测试行业“缺口”已经基本饱和。当然&#xff0c;我说的是最基础的功能测试的岗位…

良许也成为砖家啦~

大家好&#xff0c;我是良许。 没错&#xff0c;良许成为砖家啦&#xff0c;绝不是口嗨&#xff0c;有图有真相&#xff01; 有人会说&#xff0c;咦&#xff0c;这明明是严宇啊&#xff0c;跟你良许有啥关系&#xff1f; 额。。老读者应该知道良许的来历—— 鄙人真名严宇&a…

Python-datetime、time包常用功能汇总

目录基础知识时间格式有哪些&#xff1f;Python中的时间格式化时间戳datetimedatedatetimetimedeltatime常用获取今天凌晨字符串&#xff1f;将一个时间格式的字符串转为时间戳将一个时间戳转为指定格式的字符串全部代码参考基础知识 时间格式有哪些&#xff1f; 「格林威治标…

最新OpenMVG编译安装与逐命令运行增量式和全局式SfM教程

openmvg是一个轻便的可以逐步运行的SfM开源库&#xff0c;它同时实现了增量式和全局式两种算法。 说明文档地址&#xff1a;https://openmvg.readthedocs.io/en/latest/ github主页地址&#xff1a;https://github.com/openMVG/openMVG 1 编译安装 openmvg的安装比较简单&…

Windows 11 22H2 中文版、英文版 (x64、ARM64) 下载 (updated Feb 2023)

Windows 11, version 22H2&#xff0c;2023 年 2 月 更新 请访问原文链接&#xff1a;https://sysin.org/blog/windows-11/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;www.sysin.org 全新推出 Windows 11 全新 Windows 体验&#x…

论坛项目小程序和h5登录

项目中安装uview出现npm安装uview 直接报错&#xff1a;创建一个package.json配置文件在进行安装。cmd到项目。初始化一个package.json文件&#xff08;vue项目的配置文件&#xff09; npm init --yes 安装uview项目点击关注进入管页面&#xff0c;需要验证用户是否登录查用户是…

Linux学习(8)Linux文件与目录管理

以下内容转载自鸟哥的Linux私房菜 绝对路径与相对路径 绝对路径&#xff1a;路径的写法『一定由根目录 / 写起』&#xff0c;例如&#xff1a; /usr/share/doc 这个目录。相对路径&#xff1a;路径的写法『不是由 / 写起』&#xff0c;例如由 /usr/share/doc 要到 /usr/share…

Java实现在线沟通功能

文章目录1、介绍 和 特点2、整合SpringBoot2.1、导入依赖2.2、websocket 配置类2.3、消息处理类2.4、启动服务2.5、前端代码&#xff1a;张三2.6、前端代码&#xff1a;李四3、效果4、小结1、介绍 和 特点 t-io是基于JVM的网络编程框架&#xff0c;和netty属同类&#xff0c;所…

【LeetCode】剑指 Offer 14- I. 剪绳子 p96 -- Java Version

题目链接&#xff1a;https://leetcode.cn/problems/jian-sheng-zi-lcof/ 1. 题目介绍&#xff08;14- I. 剪绳子&#xff09; 给你一根长度为 n 的绳子&#xff0c;请把绳子剪成整数长度的 m 段&#xff08;m、n都是整数&#xff0c;n>1并且m>1&#xff09;&#xff0c…

计算机网络你都懂了吗

文章目录一、计算机网络的定义简单定义通用定义二、计算机网络通信过程三、什么是网络协议&#xff08;Protocol&#xff09;四、网络协议组成及功能一、计算机网络的定义 简单定义 计算机网络是一些相互连接的、自治的计算机系统的集合。 通用定义 将处于不同位置并具有独…

MySQL简介、M有SQL的存储引擎、表、字段和数据

Java知识点总结&#xff1a;想看的可以从这里进入 目录2、MySQL特性介绍2.1、MySQL简介2.2、存储引擎2.3、表、字段、数据2、MySQL特性介绍 2.1、MySQL简介 MySQL 是一个关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;于2009年被 Oracle 公司收购。它是一种关…

Hive---排序

Hive语法之排序 文章目录Hive语法之排序全局排序&#xff08;Order By&#xff09;升序降序按照别名排序多个列排序每个 Reduce 内部排序&#xff08;Sort By&#xff09;设置 reduce 个数查看设置 reduce 个数分区排序&#xff08;Distribute By&#xff09;设置 reduce 个数簇…

仅花半年时间,他从外包月薪5K到阿里月薪15K,究竟经历了什么?

背景介绍&#xff1a;“渣渣”二本&#xff0c;95年Java程序员**外包类型&#xff1a;**传统外包公司**内容简介&#xff1a;**朋友从一个传统公司是如何修仙到阿里巴巴&#xff1f;分享一些他的真实经历&#xff0c;希望对你有帮助。**学习路线&#xff1a;**基础&#xff08;…

为什么HR眼中,Python是真正的简历加分项?

教育部在发布的关于《2023届高校毕业生预计1158万 校园招聘月启动》文中明确指出&#xff1a;“2023届高校毕业生预计1158万&#xff0c;同比增加82万人”。除开考研、考公的少数同学&#xff0c;几百万大军拼命往大企业投简历&#xff0c;求职竞争十分激烈。 来源&#xff1a…

优化长尾关键词有什么好处?在线长尾关键词挖掘

​想知道为什么要使用长尾关键词&#xff1f; 好吧&#xff0c;它们可以帮助你轻松找到合适的受众。 ​ 1.获得更高的转化率 长尾关键词对于搜索特定信息更有用。使用长尾关键词时通常会获得更高的转化率&#xff0c;因为内容与受众的需求更相关。 举个例子&#xff1a;你正…

数影周报:动视暴雪疑似数据泄露,数据出境安全评估申报最新进展

本周看点&#xff1a;动视暴雪疑似员工敏感信息及游戏数据泄露&#xff1b;谷歌云计算部门&#xff1a;两名员工合用一个工位&#xff1b;数据出境安全评估申报最新进展&#xff1b;TikTok Shop东南亚商城在泰国和菲律宾公布&#xff1b;智己汽车获九大金融机构50亿元贷款签约.…

Redis:实现全局唯一ID

Redis&#xff1a;实现全局唯一ID一. 概述二. 实现&#xff08;1&#xff09;获取初始时间戳&#xff08;2&#xff09;生成全局ID三. 测试为什么可以实现全局唯一&#xff1f;其他唯一ID策略补充&#xff1a;countDownLatch一. 概述 全局ID生成器&#xff1a;是一种在【分布式…

RK3568平台开发系列讲解(驱动基础篇)中断子系统框架

🚀返回专栏总目录 文章目录 一、中断硬件的组成二、软件框架三、中断常见概念沉淀、分享、成长,让自己和他人都能有所收获!😄 📢中断是指 CPU 正常运行期间,由于内外部事件或程序预先安排的事件,引起的 CPU 暂时停止正在运行的程序, 转而为该内部或外部预先安排的事…

基于Frenet优化轨迹的⾃动驾驶动作规划⽅法

动作规划&#xff08;Motion Control&#xff09;在⾃动驾驶汽⻋规划模块的最底层&#xff0c;它负责根据当前配置和⽬标配置⽣成⼀序列的动作&#xff0c;本⽂介绍⼀种基于Frenet坐标系的优化轨迹动作规划⽅法&#xff0c;该⽅法在⾼速情况下的ACC辅助驾驶和⽆⼈驾驶都具有较强…