初探安卓MVVM框架设计

初探安卓MVVM框架设计#

一. 什么是MVVM?

MVVM是近几年流行的一种设计框架,基于该框架设计的应用程序具有良好的解耦和可扩展性,大幅降低了维护成本,提高了程序员的开发效率.在了解MVVM框架之前,我们有必要回顾一下其他设计框架.

1. MVC模式

MVC模式的意思是,软件可以分成三个部分.

视图(View):用户界面

控制器(Controller):业务逻辑

模型(Model):数据保存

各部分之间的通信方式如下.


1.View传送指令到Controller

2.Controller完成业务逻辑后,要求Model改变状态

3.Model将新的数据发送到View,用户得到反馈

所有通信都是单向的.我们传统的Android开发都是基于这种模式.每一层可以代表我们常用的如下组件:

Model层: sqlite数据库, JavaBean, SharedPreference, sdcard,获取网络数据的api等
View层: xml布局文件,自定义控件等

Controller层: Activity等
此处需要注意的是,在传统的MVC设计模式中,

Activity属于Controller层而不是View层,因为Activity即承担了数据调用,也承担了界面展示,相当于View和Model中间的协调器.很多初学者都会误认为Activity属于View层.当然,这种说法仅限用MVC模式,换做其他模式就不一定了哦!

2. MVP模式

MVC模式普及了一段时间之后,逐渐暴露出一些问题.比如我们发现,
Activity中写的代码太多,有时候一个Activity甚至达到了四五千行代码,维护起来极为不便.原因也很明显,就是Activity既参与api访问和数据调用,又参与了界面的更新,职能划分不明确,没有完全实现解耦.我们的想法是,能不能让Activity只做界面响应和更新,其他业务逻辑全部由另外一个单独模块来完成?于是MVP诞生了.

MVP模式将Controller改名为Presenter,同时改变了通信方向.

1.各部分之间的通信,都是双向的.

2.View与Model不发生联系,都通过Presenter传递.

3.View非常薄,不部署任何业务逻辑,称为"被动视图"(Passive View),即没有任何主动性,而Presenter非常厚,所有逻辑都部署在那里.

当这样调整了之后, Activity就纯粹属于View层了,所有业务逻辑全由Presenter来完成.当View界面被用户操作时(比如按钮点击), View层就会调用Presenter完成相关业务逻辑,而Presenter完成了之后,就会将结果以回调的形式传递给View层,由View层完成界面刷新.具体代码如何实现我就不多说了,因为我们今天的重点是MVVM,如果有兴趣研究的话可以在网上搜索MVP相关的例子程序,我也找了一个,仅供参考:
http://blog.csdn.net/vector_yi/article/details/24719873

3. MVVM模式

当我们采用MVP模式之后,发现Activity几乎没啥事可做了,我们的项目代码层级也清晰了,也好维护了.但是MVP也有缺点,比如,为了实现MVP,我们需要额外增加好多接口和类,比如,一个Activity需要对应一个Presenter类和Presenter接口,同时为了方便Activity和Presenter进行通信,还得再定义一个回调接口IView,也就是说,每一个Activity都需要额外增加两个接口和一个类,无疑提高了代码量.而MVVM的诞生,就解决了这个问题!

MVVM模式将Presenter改名为ViewModel,基本上与MVP模式完全一致.


唯一的区别是,它采用双向绑定(data-binding):View的变动,自动反映在ViewModel,反之亦然.

有没有注意到, MVVM和MVP几乎是一样的,唯一的不同就在于View和ViewModel之间的那根线, MVP是两根,表示View调用Presenter执行逻辑,Presenter调用View来返回数据,更新界面;MVVM中只有一根线两个箭头,代表的是View和ViewModel双向绑定,自动同步数据,无需手动调用相关方法进行通信,从而减少了代码量.而这种双向绑定的机制,都归功于谷歌推出的DataBinding的新功能.下面我们来研究一下到底什么是DataBinding.

二. 使用DataBinding构建MVVM框架

2.1 什么是DataBinding

2015 Google IO大会带来的DataBinding库使得Android开发者可以方便的实现MVVM架构模式.使用DataBinding可以改善应用程序的开发,使代码更加干净优雅.

DataBinding的使用教程在网上已经很多了,我在这里只是简单提一下最基本的用法,大家体验一下就好.如果想更深入学习的话,建议查看谷歌官方文档:https://developer.android.com/topic/libraries/data-binding/index.html

2.2 DataBinding环境配置

1.由于新版Android Studio已经内置了DataBinding的功能,为了方便开发,请确保使用AndroidStudio 1.3及以上的版本.

2.在app的build.gradle文件中添加下面的内容:

android {
....
dataBinding {
enabled =true
}
}

3.重新编译项目,配置完成.

2.3 DataBinding的基本使用

1.布局文件

根标签使用layout,在layout标签下用data标签来配置数据,例子如下:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="user" type="cn.itcast.mvvmdemo.User"/>
    </data>
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:text="@{user.firstname}"/>
        <TextView android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:text="@{user.lastname}"/>
    </LinearLayout>
</layout>

<variable name="user" type="cn.itcast.mvvmdemo.User"/>

这句话代表,声明了一个user变量,类型是cn.itcast.mvvmdemo.User,当然这个User要提前定义.

public class User {
private String firstname;
private String lastname;
public User(String firstname, Stringlastname) {
this.firstname = firstname;
this.lastname = lastname;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(Stringfirstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(Stringlastname) {
this.lastname = lastname;
}
}
<TextView android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:text="@{user.firstname}"/>

控件布局写法和以前一样,唯一不同之处在于控件内容的赋值部分.以前我们都会写一个默认值,然后再在代码中动态修改控件的值.此时已经不需要了. @{user.firstname}代表当前TextView的值取自于user对象中的firstname字段.

2. Activity代码

public class MainActivity extends AppCompatActivity {
private User user;
@Override
protected void onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding =DataBindingUtil.setContentView(this, R.layout.activity_main);
user = new User("尼古拉斯凯奇", "赵四");
binding.setUser(user);
}
}

ActivityMainBinding是DataBinding自动根据布局文件生成的类,不需要手动创建.该类的命名方式取自于布局文件的名称.比如布局文件名叫activity_main,那么生成的类名就叫ActivityMainBinding.

当使用DataBinding时,需要用DataBindingUtil来设置Activity的布局.
binding.setUser(user);表示将user对象和布局文件绑定在了一起,
user对象的所有属性值都可以同步映射到布局文件的控件中.

3. 运行效果

你会发现,我们没有像往常那样在activity中findViewById,找到控件后给动态赋值,而是通过DataBinding的方式直接将对象的值作用在了布局文件中,从而使我们的代码更加优雅和简洁.

2.3 DataBinding响应点击事件

1.首先,写一个事件处理器MyHandler

public class MyHandler {
public void onButtonClick(View view){
System.out.println("按钮被点击了");
}
}

这是一个普通的类,在onButtonClick中处理按钮点击后应该执行的操作.

2.在之前布局文件的基础上,添加一个按钮

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="user"
            type="cn.itcast.mvvmdemo.User"/>

        <variable
            name="handler"
            type="cn.itcast.mvvmdemo.MyHandler"/>
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.firstname}"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.lastname}"/>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{handler.onButtonClick}"
            android:text="点击我"
            />
    </LinearLayout>
</layout>

在data中声明handler,类型是MyHandler,在Button的onClick中定义要执行的操作.
android:onClick="@{handler.onButtonClick}"

3.在Activity中,将MyHandler设置给Binding对象.

binding.setHandler(new MyHandler());

4.运行后看效果

2.4 数据变化后同步更新界面

点击按钮之后,我们想修改一下firstname和lastname的值,然后更新界面.如果采用DataBinding的话,我们会怎么做?

1.将用户对象传递给MyHandler

public class MyHandler {
private User user;
public MyHandler(User user) {
this.user = user;
}
public void onButtonClick(View view){
System.out.println("按钮被点击了");
user.setFirstname("蒙拉丽莎");
user.setLastname("鸭蛋");
}
}

在按钮点击的时候,修改了user的firstname和lastname.如果放在往常,你肯定就立马想找到那两个TextView对象来重新设置数据,而现在,你什么都不用做,只要数据变了,界面就会立即同步更新.有这么神奇?其实你得提前做好准备,才会有这样的效果.

2.我们需要把User类调整一下:

public class User extends BaseObservable {
private String firstname;
private String lastname;
public User(String firstname, Stringlastname) {
this.firstname = firstname;
this.lastname = lastname;
}
@Bindable
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
notifyPropertyChanged(BR.firstname);
}
@Bindable
public String getLastname() {
return lastname;
}
public void setLastname(String lastname){
this.lastname = lastname;
notifyPropertyChanged(BR.lastname);
}
}

在getFirstname和getLastname两个方法中加注解@Bindable,这样的话DataBinding会自动在BR文件中生成这两个字段的id. BR文件类似于R文件,是DataBinding特有的用于维护id的一个文件. BR文件由编译器自动生成.

在setFirstname和setLastname的方法中添加notifyPropertyChanged方法,同时将你要更新的字段id传递过去.此方法用于通知系统数据已经变化,需要更新界面.

3.我们案例的最终项目结构如下图所示:

三. 总结

在学习MVVM框架时我一直有一个纠结:MVC和MVP结构很清晰,我很容易能分清楚哪个组件属于哪个模块,但到了MVVM我就有点晕了,因为网上所有介绍MVVM的文章几乎都指向了DataBinding,并没有讲到具体每一层对应哪些组件.目前就我的初步了解,我大概会这么划分:

View层: xml, Activity,自定义控件等;
Model层: sqlite数据库, JavaBean,SharedPreference, sdcard,获取网络数据的api等
ViewModel层:独立的业务逻辑处理模块,部分参与业务逻辑的JavaBean

在我们的例子项目中,MainActivity, activity_main.xml属于View层; User属于Model层; MyHandler属于ViewModle层.

不过后来我又想了一下,我们真有必要划分清楚谁是View,谁是ViewModel,谁是Model吗?程序设计本来就很复杂,难免会碰到一些模棱两可的模块,各个层都参与一下,但又不属于任何一层.我们开发应用程序是为了实现功能,我们进行框架设计是为了提高扩展性并降低维护成本,在这种大前提下,我们的细节如何处理就已经无关紧要了.事实上,当你采用了DataBinding来构建你的程序时,你其实就已经在用MVVM框架了.

当然DataBinding的用法还有很多,此文介绍的只是冰山一角,比如如何在ListView和RecyclerView中使用DataBinding,布局文件中关于DataBinding的高级用法等等,此文都没有提及.如果你想了解更多,就请关注官方文档.

关于MVVM和DataBinding的资料和博客,网上已经有很多了,由于MVVM内容确实繁杂,所以网上的文章没有特别全面的,侧重点都有所不同.当然,此文是从另一个角度来重新解读了一下MVVM模式,如果能从此文中获取对你有益的内容,会让我倍感欣慰.

Demo附件下载链接: http://pan.baidu.com/s/1pLligyf

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,817评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,329评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,354评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,498评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,600评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,829评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,979评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,722评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,189评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,519评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,654评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,940评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,762评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,993评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,382评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,543评论 2 349

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,787评论 25 707
  • Android App的设计架构:MVC,MVP,MVVM与架构经验谈1. 架构设计的目的1.1 通过设计使程序模...
    天空在微笑阅读 4,145评论 1 20
  • 1、概述 Databinding 是一种框架,MVVM是一种模式,两者的概念是不一样的。我的理解DataBindi...
    Kelin阅读 76,775评论 68 521
  • 生活里食品有保质期,药品有有效期,电器有保修期,房子有产权期,所有物质的东西都有有效期。那么父亲母亲的血液浇灌出的...
    雨雪菲阅读 620评论 4 4
  • 感受晴天一样的你 日出即明朗,日落即安详 有时闲阶小驻足,有时月色满潇湘 品味晴天一样的你 白云亦舒卷,树影亦婆娑...
    雨歇梦微凉阅读 235评论 0 1