Android学习笔记02-Android Databinding+MVVM

Posted by panthole on 2021-03-18

一、Android data binding

简单介绍

Google IO 大会上,Android 团队发布了一个数据绑定框架(Data Binding Library)。官方文档对data binding的定位是:write declarative layouts and minimize the glue code necessary to bind your application logic and layouts。即可以在layout布局文件中绑定动态数据,减少从逻辑层到视图层数据展现的java代码量(各种findViewById, setText, setTextColor,setBackground…)。

目前android data binding仅支持单向的绑定,即逻辑层数据变化,通过BaseObservable类系统可以自动通知View层进行刷新。当然,如果不采用Observable类,也可以很方便地使用代码驱动View层刷新。

支持环境

Data Binding Library 是一个 support 库,支持 Android 2.1+ 版本 (API level 7+)。

由于该框架需要使用编译器来生成很多代码,所以需要配合最新版本的 Android Studio (1.3+ )以及Android Gradle 插件(1.3.0-beta4 or higher)才能使用。目前android data binding 不能支持maven环境

使用方法

Android data binding支持直接在 layout 布局 xml 文件中绑定数据,其语法和使用方式和 JSP 中的 EL 表达式非常类似。上手非常快,而且对现有的开发框架的影响非常小。我们迅速地预览下用了data binding之后会是什么效果吧~

  1. layout xml
1
2
3
4
5
6
7
8
9
10
11
12
13
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="com.liangfeizc.databindingsamples.basic.User" />
<variable name="user" type="User" />
</data>
<!--原先的根节点(Root Element)-->
<LinearLayout ...>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.lastName}" />
</LinearLayout>
</layout>

xml的布局文件就不再单纯地展示 UI 元素,还需要定义 UI 元素用到的变量。所以,它的根节点不再是一个 ViewGroup,而是变成了 layout,并且新增了一个节点 data。data中定义xml中用到的变量,有必要的话还需要import一些类,使得xml能识别variable变量。在控件的属性里面可以直接绑定variable的属性或者方法,需要用@{}表示。

  1. 定义Observable的数据

    (1) Observable 自定义类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private static class User extends BaseObservable {
private String firstName;
private String lastName;
@Bindable
public String getFirstName() {
return this.firstName;
}
@Bindable
public String getLastName() {
return this.lastName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
notifyPropertyChanged(BR.firstName);
}
public void setLastName(String lastName) {
this.lastName = lastName;
notifyPropertyChanged(BR.lastName);
}
}

BR 是编译阶段生成的一个类,功能与 R.java 类似,用 @Bindable 标记过 getter 方法会在 BR 中生成一个 entry,当数据发生变化时,通过调用notifyPropertyChanged(BR.firstName)来通知系统 BR.firstName 这个 entry 的数据已经发生变化,然后系统会让该属性(firstName)绑定的所有 UI自动更新。

(2)定义Observable的属性

1
2
3
4
5
6
7
private static class User {
public final ObservableField<String> firstName =
new ObservableField<>();
public final ObservableField<String> lastName =
new ObservableField<>();
public final ObservableInt age = new ObservableInt();
}

谷歌官方文档中说,由于声明一个Observable 类有一些麻烦,所以谷歌还提供了一种只对属性标注Observable的方法。系统为我们提供了所有的主类型 所对应的 Observable类,例如 ObservableInt、ObservableFloat、ObservableBoolean 等等,还有一个 ObservableField 对应着引用类型。这些属性变化之后也会通知绑定的view自动更新。但是这些类型的属性的不能够再用简单的”=”进行赋值了,必须额外调用get(), set()进行读写,会有一点点麻烦。

  1. recyclerview 绑定 List

值得一提的是,android data binding的官方文档中并没有给recyclerview 绑定 List提供最佳实践案例,(然而这绑定妥妥的是刚需)。目前我们是通过自定义BindingAdapter(一个静态函数),将recyclerview和ObservableList关联。当ObservableList监听到自己发生了变化(增删改items)之后,通知我们自己写的BindingAdapter,刷新recyclerview。在后续四、Show me the Code 部分,可以看到这部分的具体实现。

1
2
3
4
5
6
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:items = "@{data.getmActList().getmList()}"
app:hasmore = "@{data.getmActList().isHasMore()}"/>
1
2
3
4
5
@BindingAdapter({"bind:items","bind:hasmore"})
public static void loadListItems(RecyclerView recyclerView, ObservableList<ActMO> items, boolean hasmore) {
((ActRecyclerAdapter)recyclerView.getAdapter()).setItems(items,hasmore);

}

优点和缺陷

优点:
(1)在xml层就处理了数据展现的逻辑,确实能够省去很多java代码!Binding类自动生成View对应的字段,不用写findViewById了,用过之后就再也不能抛弃了T__T
(2)通过Observable类和属性,可以实现数据变化,自动更新view(而且是细颗粒度地更新,只更新数据绑定的控件,而非全局更新)。通过这个特性,可以轻松地实现MVVM的模式,将View层 和 逻辑层代码解耦和。

缺陷:
(1)Data Binding Library 还处于 beta 测试阶段,所以 API 还不稳定,随时可能会修改 API 接口,同时可能存在不少 BUG。
(2)Observable 类的定义还是比较麻烦,需要写标注和通知方法,希望后续有相应的Android Studio插件自动生成
(3)xml中绑定数据的表达式还不支持代码自动提示,只能在编译后发现错误,这一块Android Studio会慢慢完善起来。

二、基于data binding 的MVVM模式

MVVM 简单介绍

在介绍MVVM之前,我们可以先总结一下目前我们的MVC开发模式是怎样工作的:Model View Controller, 是一个在复杂应用设计中组织代码的公认模式,但是在客户端开发中有着第二种含义: Massive View Controller(重量级视图控制器,我们把大部分数据展现和数据处理的代码都放在Activity和Fragment中)。它让许多程序员绞尽脑汁如何去使代码被解耦和组织地让人满意. 所以我们需要给, 并进一步分离事物,有两种可选的模式:MVP和MVVM。
(1)MVP(Model- View - Presenter) 模式:Presenter作用于model和view,它从仓库(Model)中获取数据,并格式化后让view进行显示。但是Presenter和View还是紧耦合的,从下图中可以看到View和Presenter互相持有,Presenter中包含View的渲染,会使得Presenter与特定的View的联系过于紧密。

img

(2)MVVM(Model-View-ViewModel):Mode 和View的作用与MVP模式并没有区别,但是最大的区别在于View Model通过与View的data binding(松耦合)解决了MVP中Presenter与View联系比较紧密的问题,参考下图。在这种模式之下,view-model 会在view controller上以一个属性的方式存在. view controller知道 view-model 和它的公有属性, 但是 view-model 对view controller一无所知,更不持有任何View和Context的信息。view controller负责呈现由 view-model 提供的数据 ,并展现在View上;view-model 暴漏了视图控制器所必需的最小量信息,通过数据绑定的方式,自动对应到view的更新。

img

预期收益

(1)从MVC到MVVM的转变,首先是能够将Controller减负,让代码层次更加清晰。视图层由View和ViewController组成;原先的controller被拆成了和View相关的View Controller以及与View展现方式无关的View Model。

img

(2)View和View Model 松耦合之后,View的数据展现 以及 View Model的逻辑处理可以单独进行单元测试;此外,View的展现方式改变(如本来是按键响应,现在变成了滑动响应;本来显示user.name,现在显示user.age,这些改变可以很方便的在xml或activity/fragment中进行修改,不需要修改View Model中的数据及处理逻辑。

引入Flux-Unidirectional的概念进一步拆解ViewModel

Flux提供了一种单向数据流动的思路。通过actions驱动view data的变化,view中感知到UI events之后仍然通过统一管理的actions驱动view data 的变化,避免在UI events当中直接修改 view data的值。于是actions就成为了view data变化的统一入口。其中还有一个分发器dispatcher的角色可以让actions发布给不同的数据store接收,不同的view注册监听不同store数据的变化。参考下图。

img

参考ActionsCreator+Store的设计理念, 认为view model = actions+ view data,约定view中产生的UI Events只能直接操控actions,通过actions影响view data,当view data的值改变,一种方式是通过data binding Observable特性自动更新view;另一种方式是通过消息总线,发送event给订阅的view进行更新(如过第一种方式不可用的话,就采取这种方式)。异步的或者通用的actions可以单独抽成一个单例方法类,由view model的actions调用。

三、Show me the Code

(1)首页展现活动列表(可下拉刷新、上拉加载更多),列表从mtop获取;
(2)首页每个活动可以选择“加入”或者“取消加入”。
(3)首页每个活动点击后可以进入详情,由活动类型不同,可能打开一个新的activity或者一个dialog。详情页会发起一次获取详情的mtop请求。
(4)在详情页面中也可以选择“加入”、“取消加入”、“删除”操作。
*说明:“加入”“取消加入”“删除”操作只在本地完成,所以每次从网络拉取数据之后,所有的活动将回到初始未加入的状态。

源码地址:

该源码是基于手淘脚手架的,recyclerview相关的demo放在 java/…/databindingdemos/recyclerview文件夹下

  • 逻辑架构图

    img

四、总结

在一个多月的学习期间能够尝试Flux,MVVM等开发模式,不断折腾确实很有趣~发现这些模式大部分是借鉴web或WPF开发的最佳实践,再次感受到类比和迁移的重要性。