MVC、MVP、MVVM三剑客
概述
说到Android MVVM,相信大家都会想到Google 2015年推出的DataBinding框架。然而两者的概念是不一样的,不能混为一谈。MVVM是一种架构模式,而DataBinding是一个实现数据和UI绑定的框架,是构建MVVM模式的一个工具。
之前看过很多的文章都有简单的介绍MVVM架构如何使用的,但是呢自己总是一知半解的状态,今天通过再次的学习,终于对MVVM有了一个更深刻的理解,现在就来分享下自己的心得。
MVC、MVP、MVVM
首先让我们来了解下Android中常见的开发模式。
MVC
View:XML布局文件。 Model:实体模型(数据的获取、存储、数据状态变化)。 Controller:对应于Activity,处理数据、业务和UI。
从上图可以看出,Android本身的设计还是符合MVC架构的,但是Android中纯粹作为View的XML视图功能太弱,我们大量处理View的逻辑只能写在Activity中,这样Activity就充当了View和Controller两个角色并且耦合度也很高,直接导致Activity中的代码大爆炸。
相信大多数Android开发者都遇到过一个Acitivty数以千行的代码情况吧!所以,因此这个MVC结构最终其实只是一个Model-View(Activity:View&Controller)的结构。
MVP
Model: 依然是实体模型。
View: 对应于Activity和XML,负责View的绘制以及与用户的交互。 Presenter: 负责完成View与Model间的交互和业务逻辑。
上面我们说到了mvc中的activity充当了View和Controller的角色,而MVP很好的解决了这个问题,核心理念是通过一个抽象的View接口(不是真正的View层,也就是我们常用的VIewInterface)将Presenter与真正的View层进行解耦。
Persenter持有该View接口,对该接口进行操作,而不是直接操作View层。这样就可以把视图操作和业务逻辑解耦,从而让Activity成为真正的View层。有一定的好处就是团队协作,便于开发。
不过它也有弊端:
该模式有点像分模块分包处理一样,每一个模块中都会存在大量的presenter、view、model、viewInterface这样的包名,当业务量过大时,代码量依旧是庞大臃肿,而且每个模块之间如果有相同的功能或者网络请求的时候,并不能进行代码复用,只能在不同的包中copy一次相同的代码。如果说要更新UI操作的时候需要改动的是V和P层,通过在V层添加接口在P层中实现接口来达到更新UI的效果,在一定程度上还是存在了耦合性的。
MVVM
Model: 实体模型。
View: 对应于Activity和XML,负责View的绘制以及与用户交互。 ViewModel: 负责完成View与Model间的交互,负责业务逻辑。
其实关于MVVM,之前有过错误的理解,一度认为是四层,当然现在肯定不会这样认为了。
从这个图和上面的两个图相比,明显的感觉到了这个图比之前的更为简单更为方便。其实MVVM就是MVP的升级版,MVVM的目标和思想与MVP类似,利用数据绑定(Data Binding)、依赖属性(Dependency Property)、命令(Command)、路由事件(Routed Event)等新特性,打造了一个更加灵活高效的架构。
Google在2015年就提出了要使用这种框架,那我们来看看它的神奇之处。
databinding顾名思义就是数据绑定,通过使用databinding来把数据和UI页面进行关联。
View
View层做的就是和UI相关的工作,我们只在XML、Activity和Fragment写View层的代码,View层不参与业务逻辑,也就是我们在Activity不写业务逻辑和业务数据相关的代码,更新UI通过数据绑定实现,尽量在ViewModel里面做(更新绑定的数据源即可),Activity要做的事就是初始化一些控件(如控件的颜色,添加RecyclerView的分割线),View层可以提供更新UI的接口(但是我们更倾向所有的UI元素都是通过数据来驱动更改UI),View层可以处理事件(但是我们更希望UI事件通过Command来绑定)。 简单地说:View层不做任何业务逻辑、不涉及操作数据、不处理数据,UI和数据严格的分开。
ViewModel
ViewModel只做和业务逻辑和业务数据相关的事,不做任何和UI相关的事情,ViewModel 层不会持有任何控件的引用,更不会在ViewModel中通过UI控件的引用去做更新UI的事情。 ViewModel就是专注于业务的逻辑处理,做的事情也都只是对数据的操作(这些数据绑定在相应的控件上会自动去更改UI)。 与此同时DataBinding框架支持双向绑定,可以通过双向绑定获取View层反馈给ViewModel层的数据,并对这些数据上进行操作。 关于对UI控件事件的处理,我们也希望能把这些事件处理绑定到控件上,并把这些事件的处理统一化,为此我们通过BindingAdapter对一些常用的事件做了封装,Command会把你可能需要的数据带给你,这使得我们在ViewModel层处理事件的时候只需要关心处理数据就行了。 强调:ViewModel 不做和UI相关的事。
Model
model层和mvp、mvc中的model没有什么区别,定义一个实体类进行数据的获取和存储而已 最后:Model只是一个实体类
敲黑板了,敲黑板了。重点来了
MVVM的编译方式和其他模式不同,它是实时编译的,所以我经常遇到一些莫名奇妙的问题。
PS:
1、BR类不存在(BR类生成跟我们所说的R文件是一样的性质),当我第一次使用的时候我方了,正是因为他实时编译的特性让我懵逼了。不过后面发现其实在日志的最后都会告诉你具体的原因了,大部分情况都是xml类中写错了,所以要注意了哦。
2、控件上没有值,控件没有值看你数据源有没有问题或者数据源有没有传递进去
目前遇到的也就是这些问题。
下面聊聊使用心得
1、首先我们在xml中写好相对应的界面和数据绑定关系
2、然后再Activity将页面和数据绑定起来
HomeLoanActBinding binding = DataBindingUtil.setContentView(this, R.layout.home_loan_act);
Intent intent = getIntent();
loanCtrl = new LoanCtrl(binding.apply, intent.getStringExtra(BundleKeys.LOANMONEY), intent.getStringExtra
(BundleKeys.LOANLIMIT), intent.getStringExtra(BundleKeys.REALMONEY), intent.getStringExtra(BundleKeys.FEE), intent.getStringExtra(BundleKeys
.CARDNAME), intent.getStringExtra(BundleKeys.CARDNO), intent.getStringExtra(BundleKeys.CARDID));
binding.setViewCtrl(loanCtrl);
HomeLoanActBinding binding = DataBindingUtil.setContentView(this, R.layout.home_loan_act);
通过DataBindingUtil将xml文件和activity进行关联起来,实现页面的绑定。注意
HomeLoanActBinding的生成规则是当前的activity命名加上Binding的形式。如:TestActivity生成的Binding就是TestActivityBinding.
binding.setViewCtrl(loanCtrl);
这个setViewCtrl就是将数据绑定,通过这样的两步我们就将数据和页面进行关联。
上面说到了LoanCtrl,当然他有一个自己的实体类LoanVM,这个就是我们所谓的Model类了,在这个类中我们通过使用databind的一些注解来设置一些属性的值或者是效果。
可以理解为自己写的一个方法,通过bindingAdapter来进行绑定,然后再xml中通过app:xxx(就是@bindingadapter注解的那个值)来进行调用
特别值得一提的就是在set方法中调用notifyPropertyChanged来实现数据的刷新
public void setStepEnable(boolean stepEnable) {
this.stepEnable = stepEnable;
notifyPropertyChanged(BR.stepEnable);
}
获取你会问BR.stepEnable怎么来的,这个其实就是VM的属性值。set方法中能做的不仅如此,如果说有一些逻辑的判断也是可以在这个里面进行的处理的
public void setPhone(String phone) {
this.phone = phone;
checkPhoneInput();
notifyPropertyChanged(BR.phone);
}
private void checkPwdInput() {
if (TextUtil.isEmpty(pwd) || pwd.length() < 6) {
setEnable(false);
} else {
setEnable(true);
}
}
是不是很强大,之前就算是使用MVP的模式我们都需要在V层将这种逻辑先写出来,做到了真正的解耦。不过弊端就是需要你要熟悉它的用法和了解它的编译,能很好的解决bug。
其他关于MVVM的基本资料就请大家自行查阅资料咯。以上就是我对MVVM的一些心得总结。
- 看我如何发现Google云平台漏洞并获得$7500赏金
- Go语言写Web 应用程序
- 小萝莉说Crash(一):Unrecognized selector sent to instance xxxx
- 游戏服务器之多线程发送(上)
- 游戏服务器之多线程发送(中)
- 游戏服务器之多线程发送(下)
- 【团队分享】手机QQ:升级iOS8.3后,发图就崩,为哪般?
- golang 字符串操作实例
- 【团队分享】刀锋铁骑:常见Android Native崩溃及错误原因
- OpenShift企业版安装:单Master集群
- http线程池的设计与实现(c++)
- iOS崩溃堆栈符号化,定位问题分分钟搞定!
- Duang~ Android堆栈慘遭毁容?精神哥揭露毁容真相!
- Java学习笔记第一篇:坦克大战游戏
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Python自学成才之路 带有参数的装饰器
- Grafana 将默认的元数据库 sqlite 改为 mysql
- 使用IDEA整合spring4+spring mvc+hibernate
- springmvc中@PathVariable和@RequestParam的区别
- centos7 安装mysql5.6
- bashdb安装及调试shell脚本
- linux awk指令详解
- linux sed指令详解
- Grafana使用zabbix自定义模板
- linux shell之变量的使用规则
- 第11期:压缩表
- grafana-zabbix插件安装和配置zabbix mysql
- grafana使用教程之API key
- Grafana使用教程之安装
- Java基础数据类型之包装类equals和==详解