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 条评论
登录 后参与评论

相关文章

来自专栏Janti

记一次线程池调优经历

背景: 最近的一个项目需要用到招标,临时加了给我们的系统增加了一个性能需求,多少呢? 一秒钟300次NTP(不知道ntp的同学可以百度一下),平均3ms一次啊,...

3806
来自专栏京东技术

Android Architecture Paging Library详解 | Google I/O大会上的最新发布

Android高级工程师,6年以上开发经验,有丰富的代码重构和架构设计经验,负责京东商城我的京东的开发工作,热衷于学习和研究新技术。

1522
来自专栏Android开发小工

探索Android架构的DataLayer层(DataManager方式)具体实现

我想无论是移动端工程师还是服务器工程师,技术成长路线大致都是先熟练(不是熟悉,hahaha)掌握了编程语言、现行开发流行框架和项目业务逻辑后,向着架构师的方向发...

612
来自专栏Android群英传

Andromeda:适用于多进程架构的组件通信框架(上)

882
来自专栏Android 开发者

ViewModel 和 LiveData:为设计模式打 Call 还是唱反调?

1923
来自专栏java架构师

MVC的Model Binder总结

今年一直在做一个mvc的项目,现在已经初具规模,适时的总结一下,也算是对MVC框架的一次更深入的研究。 由于时间以及成员技术水平的原因,在开发过程中,一直秉持:...

3209
来自专栏草根专栏

使用C# (.NET Core) 实现适配器模式 (Adapter Pattern) 和外观模式 (Facade Pattern)

本文的概念内容来自深入浅出设计模式一书 现实世界中的适配器(模式) ? 我带着一个国标插头的笔记本电脑, 来到欧洲, 想插入到欧洲标准的墙壁插座里面, 就需要用...

2676
来自专栏EAWorld

RESTful API教程:学习关键的Web服务设计原则

原题:RESTful APIs tutorial: Learn key web service design principles

461
来自专栏逸鹏说道

基础才是重中之重~多线程的代价~我的内存都被吃了!

异步操作是.net4.5推出的新名词,事实上,这东西早就有了,它归根结底是通过线程池来实现的,即将一个大任务分成多个小任何块,每个线程并行处理其中的一个,完成后...

2697
来自专栏拂晓风起

node.js WebService异常处理(domain)以及利用domain实现request生命周期的全局变量

1343

扫描关注云+社区