一、MVC模式
1.基本概念
MVC(Model—View—Controller 模型—视图—控制器)模式,用一种业务逻辑、数据、界面显示分离的方式组织代码,在改进和个性化定制界面及用户交互的同时,无需重新编写业务逻辑。所有通信都是单向的。
MVC角色定义:
模型层: 针对业务模型建立的数据结构和相关的类,即为Model。Model是与View无关,而与业务相关的。
视图层: 一般采用XML文件或Java代码进行界面的描述,也可以使用Javascript+HTML等方式作为View层。
控制层: 控制层通常在Activity、Fragment或由它们控制的其他业务类中。
- View 传送指令到 Controller
- Controller 完成业务逻辑后,要求 Model 改变状态
- Model 将新的数据发送到 View,用户得到反馈
Activity并非标准的MVC模式中的Controller,它的首要职责是加载应用的布局和初始化用户界面,接受并处理来自用户的操作请求,进而做出响应。随着界面及其逻辑复杂度不断提升,Activity类的职责不断增加,以至变得庞大臃肿。
2.优缺点
优点:
- 耦合性低
- 重用性高
- 生命周期成本低
- 部署快
- 可维护性高
- 有利软件工程化管理
缺点:
- 没有明确的定义
- 不适合小型,中等规模的应用程序
- 增加系统结构和实现的复杂性
- 视图与控制器间的过于紧密的连接
- 视图对模型数据的低效率访问
- 一般高级的界面工具或构造器不支持模式
- 视图与模型相互耦合,不易开发和维护
在MVC里,View是可以直接访问Model的。因此,View里会包含Model信息,不可避免的还要包括一些业务逻辑。 在MVC模型里,更关注的Model的不变,而同时有多个对Model的不同显示及View。所以,在MVC模型里,Model不依赖于View,但是View是依赖于Model的。不仅如此,因为有一些业务逻辑在View里实现了,导致要更改View也是比较困难的,至少那些业务逻辑是无法重用的。
二、MVP模式
1.基本概念
MVP模式将应用分为三层 :Model、View、Presenter。MVP 模式将 Controller 改名为 Presenter,同时改变了通信方向。
MVP角色定义:
模型层: 封装各种数据来源(如网络、数据库等),对Presenter层提供简单易用的接口。
视图层: 包含界面相关的功能,例如各种Activity、Fragment、View、Adapter等,该层专注于用户的交互吗,实现设计师给出的界面、动画等交互效果。View层一般会持有Presenter层的引用,或可以通过依赖注入(如Dagger)的方式获得Presenter的实例,并将非UI的路基操作委托给Presenter。
控制层: 充当中间人的角色, 用来隔离View层和Model层,该层是通过从View层剥离控制逻辑部分而形成的,主要负责View层和Model层的控制和交互。
- 各部分之间的通信,都是双向的。
- View 与 Model 不发生联系,都通过 Presenter 传递。
- View 非常薄,不部署任何业务逻辑,称为"被动视图"(Passive View),即没有任何主动性,而 Presenter非常厚,所有逻辑都部署在那里。
2.优缺点
优点:
- 模型与视图完全分离,我们可以修改视图而不影响模型
- 可以更高效地使用模型,因为所有的交互都发生在一个地方——Presenter内部
- 我们可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑。这个特性非常的有用,因为视图的变化总是比模型的变化频繁。
- 三层的交互都是基于接口实现的,有助于对Presenter进行单元测试,同时由于是面向接口编程,只需要事先定义好接口,每一层的事先可以交由不同开发人员并行实现,最终再一起联调。
缺点:
- 增加代码类的数量
- 由于进行了三层划分,函数的调用栈变深了,如果开发人员没能非常清楚地了解哪一层具体该负责哪些功能,那么可能存在因为层次职责辨认不清等原因导致不同层之间代码乱入,从而没能达到MVP充分解耦各层的目的。
- 由于对视图的渲染放在了Presenter中,所以视图和Presenter的交互会过于频繁。如果Presenter过多地渲染了视图,往往会使得它与特定的视图的联系过于紧密。一旦视图需要变更,那么Presenter也需要变更了。
3.MVP与MVC的区别
MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责视图。作为一种新的模式,MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过中间人Presenter 来进行间接通信的,所有的交互都发生在Presenter内部,Presenter和View以及Model的交互都是通过接口进行的,通常View与Presenter是一对一的,复杂的View可能需要多个Presenter来共同处理。而在MVC中View会从直接Model中读取数据而不是通过 Controller,而且Controller是基于行为的,可以被多个View共享。
三、MVVM模式
1.基本概念
MVP中随着业务逻辑的增加,UI的改变多的情况下,会有非常多的跟UI相关的 Case,这样就会造成View的接口会很庞大。而MVVM就解决了这个问题,通过双向绑定的机制,实现数据和UI内容,只要想改其中一方,另一方都能够及时更新的一种设计理念,这样就省去了很多在View层中写很多 Case 的情况,只需要改变数据即可。Angular 和 Ember 都采用这种模式。
相比MVP模式,MVVM将Presenter改为了ViewModel,同时实现了View和ViewModel的双向绑定。View层的变化会自动导致ViewModel发生变化,ViewModel的数据变化也会自动实现View的刷新,开发者可以不用直接处理View和数据的更新操作,MVVM框架会自动完成。
MVVM角色定义:
模型层: 负责数据的存储、读取网络数据、操作数据库数据以及I/O,一般会有一个ViewModel对象来调用获取这一部分的数据。
视图层: 仅做和UI相关的工作,我们只在XML、Activity、Fragment写View层的代码。View层不做任何业务逻辑、不涉及操作数据、不处理数据、UI和数据严格的分开。
控制层: 只做和业务逻辑和业务数据相关的事,不做任何和UI、控件相关的事,不会持有任何控件的引用,更不会通过UI控件的引用去做更新UI的事情。专注于业务的逻辑处理,操作的也都是对数据进行操作,数据源绑定在相应的控件上会自动去更改UI,开发者不需要关心更新UI的事情。
2.优缺点
优点:
- 使得View层和业务逻辑层相互更加独立,MVP的Presenter层会持有View层引用,而MVVM的ViewModel则没有,由于这种低耦合,一个ViewModel可以在多个View中使用,可用于频繁的UI更改
- 使得做UI测试和单元测试更加简单。
缺点:
- 数据绑定使得 Bug 很难被调试,使得一个位置的 Bug 被快速传递到别的位置,要定位原始出问题的地方就变得不那么容易了。
- 对于简单的UI界面,使用MVVM是大材小用;对于大型复杂界面,需要保存的状态数据比较多,ViewModel的创建会比较繁琐,也会造成内存消耗。
3.DataBinding
MVVM中,ViewModel不会对UI进行更新,而是转交给Data Binding来完成。它的原理也是使用编译时注解,生成绑定代码,监听数据变化而更新UI。
DataBinding函数库引入方法:
在build.gradle文件中加入如下配置:
android{
dataBinding{
enabled = ture
}
}
Data Binding表达式:
Data Binding布局文件以作为根布局标签,里面又包含data和view两个标签,其中data标签用于实现数据绑定,view标签是在未使用DataBinding时布局文件的根标签。
数据绑定:
编译生成的绑定类MainActivityBinding是根据布局文件生成的,类名也是根据其命名的。这个类包含了所有的映射,自动实现数据绑定。数据绑定的用法:
proteected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
User user = new User("Test", "User");
binding.setUser(user);
}
也可以通过inflate方式:
MainActivityBinding binding = MainActivityBinding.inflate(getLayoutInflator());
若在ListView或RecyclerView的adapter中使用数据绑定,则可通过:
ListItemBinding binding = ListItemBinding.inflate(LayoutInflater,viewGroup,false);
事件绑定:
在DataBinding的布局文件中也可进行事件绑定,类似于android:onClick可以绑定Java方法。
使用步骤总结:
- 环境搭建: 在build.gradle中开启数据绑定。
- 数据: 创建一个数据类。
- 布局: 创建一个MainActivity对应的布局文件。
- 绑定: Build -->Make Project,编译生成一个绑定类。在MainActivity中使用MainActivityBinding完成数据与布局的绑定。
- 事件监听: 定义一个事件处理类,在布局文件中配置事件监听属性。在MainActivity的onCreate()方法中创建一个事件处理对象并传给MainActivityBinding。
- 自动更新: 对数据类型改造,在setter方法中调用notifyPropertyChanged()方法通知属性修改,进而更新UI。