[最全]Android安卓架构MVC、MVP、MVVM之间的区别和联系(图解+案例+源码)
一、问题背景
博主最近在准备春招面试中介绍自己简历中Android项目的MVP架构,但是博主发现若自身不彻底弄懂Android安卓架构MVC、MVP、MVVM之间的区别和联系,博主将无法准确地向面试官讲解自己Android项目的MVP架构,因此博主浏览了相关文章,做出了以下总结。
二、Android安卓架构MVC、MVP、MVVM
1.MVC(Model View Controller)
MVC各字母的全称及含义:
- Model:代表我们的数据模型,管理数据状态,比如Android项目中Java Bean。
- View:视图,即呈现给用户的UI,比如Android项目中的layout.xml文件、Activity和Fragment。
- Controller:控制者,负责处理用户与app之间的交互,包含业务逻辑。所以Controller是Model与View的中介,比如Android项目中Activity和Fragment。
MVC,Model View Controller,是软件架构中最常见的一种框架。简单来说就是通过controller的控制去操作model层的数据,并且返回给view层展示,具体见下图:
当用户触发事件的时候,view层会发送指令到controller层,接着controller去通知model层更新数据,model层更新完数据以后直接显示在view层上,这就是MVC的工作原理。
1.1 MVC的优缺点
MVC缺点:
- View与Model之间还存在依赖关系,Controller很重很复杂。
由上面的MVC架构图可知,view层和model层是相互可知的,这意味着两层之间存在耦合,耦合对于一个大型程序来说是非常致命的,因为这表示开发,测试,维护都需要花大量的精力。 - 在Android中Activity即是View又是Controller,所以会很复杂。
xml作为view层,控制能力实在太弱了,你想去动态的改变一个页面的背景,或者动态的隐藏/显示一个按钮,这些都没办法在xml中做,只能把代码写在activity中,造成了activity既是controller层,又是view层的这样一个窘境。大家回想一下自己写的代码,如果是一个逻辑很复杂的页面,activity或者fragment是不是动辄上千行呢?这样不仅写起来麻烦,维护起来更是噩梦。
1.2 Android中的MVC
那具体到Android上是怎么样一个情况呢?
大家都知道一个Android工程有什么对吧,有java的class文件,有res文件夹,里面是各种资源,还有类似manifest文件等等。对于原生的Android项目来说,layout.xml里面的xml文件就对应于MVC的view层,里面都是一些view的布局代码,而各种java bean,还有一些类似repository类就对应于model层,至于controller层嘛,当然就是各种activity咯。大家可以试着套用我上面说的MVC的工作原理是理解。比如你的界面有一个按钮,按下这个按钮去网络上下载一个文件,这个按钮是view层的,是使用xml来写的,而那些和网络连接相关的代码写在其他类里,比如你可以写一个专门的networkHelper类,这个就是model层,那怎么连接这两层呢?是通过button.setOnClickListener()这个函数,这个函数就写在了activity中,对应于controller层。是不是很清晰。
大家想过这样会有什么问题吗?显然是有的,不然为什么会有MVP和MVVM的诞生呢,是吧。问题就在于xml作为view层,控制能力实在太弱了,你想去动态的改变一个页面的背景,或者动态的隐藏/显示一个按钮,这些都没办法在xml中做,只能把代码写在activity中,造成了activity既是controller层,又是view层的这样一个窘境。大家回想一下自己写的代码,如果是一个逻辑很复杂的页面,activity或者fragment是不是动辄上千行呢?这样不仅写起来麻烦,维护起来更是噩梦。(当然看过Android源码的同学其实会发现上千行的代码不算啥,一个RecyclerView.class的代码都快上万行了呢。。)
MVC还有一个重要的缺陷,大家看上面那幅图,view层和model层是相互可知的,这意味着两层之间存在耦合,耦合对于一个大型程序来说是非常致命的,因为这表示开发,测试,维护都需要花大量的精力。
正因为MVC有这样那样的缺点,所以才演化出了MVP和MVVM这两种框架。
2.MVP(Model View Presenter)
MVP各字母的全称及含义:
- Model:代表我们的数据模型,管理数据状态。
- View:视图,即呈现给用户的UI,并且负责与客户进行交互。比如我们的XML/Activity/Fragment。
- Presenter:主持者,Presenter通过View接收用户的输入,然后在Model的帮助下处理用户的数据并将结果传递回View。Presenter通过接口与View进行通信。接口在presenter类中定义,它传递所需的数据。Activity/Fragment 及其他View视图组件实现此接口获得他们想要的数据并呈现数据。
MVP作为MVC的演化,解决了MVC不少的缺点,对于Android来说,MVP的model层相对于MVC是一样的,而activity和fragment不再是controller层,而是纯粹的view层,所有关于用户事件的转发全部交由presenter层处理。下面还是让我们看图:
从图中就可以看出,最明显的差别就是view层和model层不再相互可知,完全的解耦,取而代之的presenter层充当了桥梁的作用,用于操作view层发出的事件传递到presenter层中,presenter层去操作model层,并且将数据返回给view层,整个过程中view层和model层完全没有联系。看到这里大家可能会问,虽然view层和model层解耦了,但是view层和presenter层不是耦合在一起了吗?其实不是的,对于view层和presenter层的通信,我们是可以通过接口实现的,具体的意思就是说我们的activity,fragment可以去实现定义好的接口,而在对应的presenter中通过接口调用方法。不仅如此,我们还可以编写测试用的View,模拟用户的各种操作,从而实现对Presenter的测试。这就解决了MVC模式中测试,维护难的问题。
当然,其实最好的方式是使用fragment作为view层,而activity则是用于创建view层(fragment)和presenter层(presenter)的一个控制器。
2.1MVP的优缺点
优点:
- 将View与Model解耦,方便进行单元测试。
- Presenter层可通过实现接口与View层通信从而避免Presenter层与View层耦合。
- activity和fragment不再是controller层,而是纯粹的view层。
缺点:虽然是MVC模式的演变,但Presenter依旧很‘重’很复杂。
3.MVVM(Model View ViewModel)
MVVM各字母的全称及含义:
- Model:代表我们的数据模型,管理数据状态。
- View:视图,即呈现给用户的UI,并且负责与客户进行交互。比如我们的XML/Activity/Fragment。
- ViewModel:如上图所示,ViewModel与Presenter的区别,在MVVM中,View引用持有ViewModel,但ViewModel得不到任何关于View的信息。所以View与ViewModel之间存在着一对多的关系,一个View可以持有多个ViewModel。
从图中看出,它和MVP的区别貌似不大,只不过是presenter层换成了viewmodel层,还有一点就是view层和viewmodel层是相互绑定的关系,这意味着当你更新viewmodel层的数据的时候,view层会相应的变动ui。
三、Android安卓架构MVC、MVP、MVVM的源码实例
我们分别用MVC、MVP、MVVM设计模式来实现一个用户登入功能的界面,下方演示动图的主界面中有3个按钮,点击各个按钮将跳转到分别以MVC、MVP、MVVM架构实现的登录功能的界面,演示动图如下:
强烈建议您直接从该源码实例的Github网址中下载项目源码,并在Android Studio中浏览源代码并运行项目,这样即可详细地了解MVC、MVP、MVVM之间的区别与联系。
1.源码实例中的MVC架构
我们在Android Studio中可看到该源码实例项目的Android项目结构:
其中,该项目的MVC架构的View便是上图红框选中的activity_mvc_login.xml
文件和include_login_view.xml
文件;MVC架构的Controller便是MvcLoginActivity.java
,而MVC架构的Model便是User.java
。
MVC架构中为Model的User.java
的代码如下,我们可以看到User.java
是一个典型的Java Bean:
package com.example.mvcmvpmvvm.mvc.Model;public class User {private String userName;private String password;public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}
}
MVC架构中为Controller的MvcLoginActivity.java
的代码如下:
package com.example.mvcmvpmvvm.mvc.controller;import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;import com.example.mvcmvpmvvm.R;
import com.example.mvcmvpmvvm.mvp.model.User;/*** @author jere*/
public class MvcLoginActivity extends AppCompatActivity {private EditText userNameEt;private EditText passwordEt;private User user;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_mvc_login);user = new User();userNameEt = findViewById(R.id.user_name_et);passwordEt = findViewById(R.id.password_et);Button loginBtn = findViewById(R.id.login_btn);loginBtn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {login(userNameEt.getText().toString(), passwordEt.getText().toString());}});}private void login(String userName, String password) {if (userName.equals("jere") && password.equals("123")) {user.setUserName(userName);user.setPassword(password);Toast.makeText(MvcLoginActivity.this,userName + " Login Successful",Toast.LENGTH_SHORT).show();} else {Toast.makeText(MvcLoginActivity.this,"Login Failed",Toast.LENGTH_SHORT).show();}}
}
由上述MvcLoginActivity.java
的代码,我们可以看到MvcLoginActivity.java
既是View又是Controller。
其符合MVC的缺点:
- View与Model之间还存在依赖关系,Controller很‘重’很复杂。
由上面的MVC架构图可知,view层和model层是相互可知的,这意味着两层之间存在耦合,耦合对于一个大型程序来说是非常致命的,因为这表示开发,测试,维护都需要花大量的精力。 - 在Android中Activity即是View又是Controller,所以会很复杂。
xml作为view层,控制能力实在太弱了,你想去动态的改变一个页面的背景,或者动态的隐藏/显示一个按钮,这些都没办法在xml中做,只能把代码写在activity中,造成了activity既是controller层,又是view层的这样一个窘境。大家回想一下自己写的代码,如果是一个逻辑很复杂的页面,activity或者fragment是不是动辄上千行呢?这样不仅写起来麻烦,维护起来更是噩梦。
2.源码实例中的MVP架构
Model层:
public interface IUserBiz {boolean login(String userName, String password);
}
package com.example.mvcmvpmvvm.mvp.model;/*** @author jere*/
public class User {private String userName;private String password;public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}
}
package com.example.mvcmvpmvvm.mvp.model;/*** @author jere* 其中IUserBiz的Biz代表Business*/
public class UserBiz implements IUserBiz {@Overridepublic boolean login(String userName, String password) {if (userName.equals("jere") && password.equals("123")) {User user = new User();user.setUserName(userName);user.setPassword(password);return true;}return false;}
}
Presenter层:
package com.example.mvcmvpmvvm.mvp.presenter;import com.example.mvcmvpmvvm.mvp.model.User;
import com.example.mvcmvpmvvm.mvp.model.UserBiz;
import com.example.mvcmvpmvvm.mvp.view.IMvpLoginView;/*** @author jere*/
public class LoginPresenter{private UserBiz userBiz;private IMvpLoginView iMvpLoginView;public LoginPresenter(IMvpLoginView iMvpLoginView) {this.iMvpLoginView = iMvpLoginView;this.userBiz = new UserBiz();}public void login() {String userName = iMvpLoginView.getUserName();String password = iMvpLoginView.getPassword();boolean isLoginSuccessful = userBiz.login(userName, password);iMvpLoginView.onLoginResult(isLoginSuccessful);}}
View层:
public interface IMvpLoginView {String getUserName();String getPassword();void onLoginResult(Boolean isLoginSuccess);
}
package com.example.mvcmvpmvvm.mvp.view;import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;import com.example.mvcmvpmvvm.R;
import com.example.mvcmvpmvvm.mvp.presenter.LoginPresenter;/*** @author jere*/
public class MvpLoginActivity extends AppCompatActivity implements IMvpLoginView{private EditText userNameEt;private EditText passwordEt;private LoginPresenter loginPresenter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_mvp_login);userNameEt = findViewById(R.id.user_name_et);passwordEt = findViewById(R.id.password_et);Button loginBtn = findViewById(R.id.login_btn);loginPresenter = new LoginPresenter(this);loginBtn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {loginPresenter.login();}});}@Overridepublic String getUserName() {return userNameEt.getText().toString();}@Overridepublic String getPassword() {return passwordEt.getText().toString();}@Overridepublic void onLoginResult(Boolean isLoginSuccess) {if (isLoginSuccess) {Toast.makeText(MvpLoginActivity.this,getUserName() + " Login Successful",Toast.LENGTH_SHORT).show();} else {Toast.makeText(MvpLoginActivity.this,"Login Failed",Toast.LENGTH_SHORT).show();}}
}
3.源码实例中的MVVM架构
ViewModel层:
public class LoginViewModel extends ViewModel {private User user;private MutableLiveData<Boolean> isLoginSuccessfulLD;public LoginViewModel() {this.isLoginSuccessfulLD = new MutableLiveData<>();user = new User();}public MutableLiveData<Boolean> getIsLoginSuccessfulLD() {return isLoginSuccessfulLD;}public void setIsLoginSuccessfulLD(boolean isLoginSuccessful) {isLoginSuccessfulLD.postValue(isLoginSuccessful);}public void login(String userName, String password) {if (userName.equals("jere") && password.equals("123")) {user.setUserName(userName);user.setPassword(password);setIsLoginSuccessfulLD(true);} else {setIsLoginSuccessfulLD(false);}}public String getUserName() {return user.getUserName();}
}
View层:
public class MvvmLoginActivity extends AppCompatActivity {private LoginViewModel loginVM;private EditText userNameEt;private EditText passwordEt;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_mvvm_login);userNameEt = findViewById(R.id.user_name_et);passwordEt = findViewById(R.id.password_et);Button loginBtn = findViewById(R.id.login_btn);loginBtn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {loginVM.login(userNameEt.getText().toString(), passwordEt.getText().toString());}});loginVM = ViewModelProviders.of(this).get(LoginViewModel.class);loginVM.getIsLoginSuccessfulLD().observe(this, loginObserver);}private Observer<Boolean> loginObserver = new Observer<Boolean>() {@Overridepublic void onChanged(@Nullable Boolean isLoginSuccessFul) {if (isLoginSuccessFul) {Toast.makeText(MvvmLoginActivity.this,loginVM.getUserName() + " Login Successful",Toast.LENGTH_SHORT).show();} else {Toast.makeText(MvvmLoginActivity.this,"Login Failed",Toast.LENGTH_SHORT).show();}}};
}
本文参考文献:
[1]教你认清MVC,MVP和MVVM三者的区别
[2]MVC、MVP、MVVM,我到底该怎么选?
[3]Android MVC MVP MVVM简单例子
[4]android 架构设计之MVC MVP MVVM
[5]Android框架MVC、MVP和MVVM探究(图解+案例+附源码)
[最全]Android安卓架构MVC、MVP、MVVM之间的区别和联系(图解+案例+源码)相关推荐
- 【Android】Android安卓架构MVC、MVP、MVVM之间的区别和联系(图解+案例+源码)
https://github.com/SETANDGET/AndroidArchitectureDemo 代码 一.问题背景 二.Android安卓架构MVC.MVP.MVVM 1.MVC(Model ...
- Android 开发架构-MVC MVP MVVM详解
何为架构 架构,即程序的逻辑组织结构,是指导开发过程中划分程序逻辑模块的关键,好的架构要使程序达到高内聚低耦合的设计目标.例如一个人,身体的骨骼即为身体的架构,有了基本骨架之后,才可以决定在头颅里开发 ...
- MVC, MVP, MVVM比较以及区别(下)
上一篇得到大家的关注,非常感谢.一些朋友评论中,希望快点出下一篇.由于自己对于这些模式的理解也是有限,所以这一篇来得迟了一些.对于这些模式的比较,是结合自己的理解,一些地方不一定准确,但是只有亮出自己 ...
- mvc mvp mvvm比较以及区别
MVC, MVP和MVVM都是用来解决界面呈现和逻辑代码分离而出现的模式. 软件中最核心的,最基本的东西是什么? 是的,是数据.我们写的所有代码,都是围绕数据的. 围绕着数据的产生.修改等变化,出现了 ...
- MVC MVP MVVM原理和区别
MVC是指Modle,View和Controller,将界面,业务逻辑和控制器分开,是一种低耦合的设计方式,适用于简单的应用开发. 这种设计模式最简单,但问题有三: (1).View和Model相互可 ...
- Android App的设计架构:MVC,MVP,MVVM与架构经验谈
本文转载自https://www.tianmaying.com/tutorial/AndroidMVC,原文作者周鸿博. 和MVC框架模式一样,Model模型处理数据代码不变在Android的App开 ...
- Android架构设计之MVC/MVP/MVVM浅析
目录 写在前面 一.案例演示 二.MVC模式 2.1.MVC简介 2.2.MVC模式的使用 2.3.MVC模式的缺点 三.MVP模式 3.1.MVP简介 3.2.MVP模式的作用 3.3.MVP模式的 ...
- Android中常见的MVC/MVP/MVVM模式
Android中常见的MVC/MVP/MVVM模式 经典MVC 在1979年,经典MVC模式被提出. 在当时,人们一直试图将纯粹描述思维中的对象与跟计算机环境打交道的代码隔离开来,而Trygve Re ...
- Android 系统(77)---MVC,MVP,MVVM的区别
MVC,MVP,MVVM的区别 一.MVC 软件可以分为三部分 1.Model:模型层,负责处理数据的加载或者存储 2. View:视图层,负责界面数据的展示,与用户进行交互 3.Controll ...
最新文章
- 江苏:5G先行,智慧江苏再进一步
- MySQL的多表查询(笛卡尔积原理)
- Ubuntu/Centos 等linux终端忽略大小写提示
- 【Docker】Docker 启动prometheus报错 parsing YAML file /etc/prometheus/prometheus.yml: yaml: unmarshal
- Docker 容器的通信(十二)
- java获取上周任意一天的日期
- python爬取邮件内容_利用Python爬取获取博客园文章定时发送到邮箱
- jsp开发项目中的问题解决
- kali免杀工具Veil Evasion
- Java自己编名字的百家姓罗列
- Excel如何批量删除所有空格
- https开头的网址是什么意思_网址是什么意思?基础知识普及
- 阿里六面(总结他人事迹)
- curl ip.sb查询公网ip
- 【ubuntu】Ubuntu系统下安装石墨文档
- python-字典练习3 -数字重复统计
- 微软正式发布 Windows 11 SE
- 蓝桥杯 算法训练 - 连续正整数的和 78这个数可以表示为连续正整数的和,1+2+3,18+19+20+21,25+26+27。 输入一个正整数 n(<=10000) 输出 m 行(n有m
- ue4 通过指定路径加载资源_求助!加载pak资源到内存后,如何引用资源显示出来到地图中?...
- 寻梦,总免不了迷茫1
热门文章
- 【学习】02 今日头条爬虫-采集和下载关键词“新垣结衣”的图集图片
- oracle 密码文件在哪里看,Oracle数据库密码文件的使用
- 数据库密码忘记了,如何修改密码
- 软考:信息安全工程师5(应用安全)
- 人生少走弯路的十条忠告(转)
- P3243 [HNOI2015]菜肴制作(拓扑排序)
- changes not staged for commit 解决办法
- JavaWEB ServletContext 接口详解
- 微信小程序在线客服接入
- 华为系统鸿蒙多少个月不卡,华为鸿蒙系统正式领证!你会支持华为手机吗?