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的一些心得总结。

原文发布于微信公众号 - 猿份到(sparkcliff)

原文发表时间:2017-10-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java3y

AJAX应用【股票案例、验证码校验】

股票案例 我们要做的是股票的案例,它能够无刷新地更新股票的数据。当鼠标移动到具体的股票中,它会显示具体的信息。 我们首先来看一下要做出来的效果: ? 服务器端分...

38110
来自专栏听雨堂

水晶报表文本在web中无法两端对齐

        在Web上利用水晶报表显示一段文本,用的是动态加载rpt的方法,结果出来的文本效果如下: ?         右边很不齐,于是回到水晶报表10...

1788
来自专栏熊二哥

AngularJS快速入门

记得第一次听说AngularJS这项很赞的Web的前端技术,那时还是2014年,年中时我们我的一个大牛兄弟当时去面试时,被问到了是否熟悉该技术,当时他了解和使用...

1865
来自专栏安恒信息

安恒信息研究员发现Struts 2高危漏洞,Apache官方致谢

ApacheStruts 2是世界上最流行的Java Web服务器框架之一。不久之前,安恒信息风暴中心安全研究员在Struts 2上发现了一枚远程代码执行漏洞,...

2355
来自专栏林德熙的博客

win10 uwp win2d 离屏渲染

离屏渲染(Offscreen drawing)是一个不错的科技,在系统有空的时候,提前先画出部分界面。这样在需要直接渲染的时候就可以直接拿出来而不需要等待进行渲...

342
来自专栏hbbliyong

基于Three.js的360度全景--photo-sphere-viewer--简介

这个是基于three.js的全景插件  photo-sphere-viewer.js  ————————————————————————————————————...

4419
来自专栏Nian糕的私人厨房

Vue2.0 歌手数据获取及排序

本次的系列博文的知识点讲解和代码,主要是来自于 黄轶 在慕课网的 Vue 2.0 高级实战-开发移动端音乐WebApp 课程,由个人总结并编写,其代码及知识点...

573
来自专栏腾讯NEXT学位

React 16 加载性能优化指南(下)

| 导语 本篇干货是接本周三React 16 加载性能优化指南(上)推文。 关于 React 应用加载的优化,其实网上类似的文章已经有太多太多了,随便一搜就是...

912
来自专栏我杨某人的青春满是悔恨

试水 Mac 开发 —— 代码生成器

国庆的时候写了个小工具来将 JSON 转化成 Model,也算是我的第一个 Mac App,今天重构了下,顺便跟大家分享下 Mac 开发有多不方便……

502
来自专栏進无尽的文章

动画| 类似Windows的气泡屏保效果

有时候我们在打印一些CG类型的变量是,无法打印,利用UIKIT中的API可以很方便的实现 字符串和CG变量之间的转换。

912

扫码关注云+社区