本文假设读者对MVVM模式以及DataBinding的使用有基本的了解,在此基础上再深入讨论DataBinding在MVVM模式中承担的功能,以及其实现的原理。
DataBinding与MVVM
这一节主要讨论DataBinding在MVVM中起到的作用,先来看下MVVM模式的基本结构图:

注意到View与ViewModel有一个Binding的关系。Binding的这一层关系使得我们的ViewModel与View可以实现双向通信。如果不借助DataBinding框架,我们自己用framework原生的能力的话,容易想到的办法就是在ViewModel中将View与ViewModel绑定,毕竟View主要是由xml来实现的,能力有限。
而DataBinding框架就是用来解决这一问题的,它可以将绑定的这部分代码从ViewModel中剥离出来,迁移到xml文件中,使ViewModel可以专注于业务逻辑。
除此之外,如果你实现过不基于Databinding的MVVM,那可能会遇到过这样的问题,对于数据分解映射多个View(即一个对象的多个属性分别独立对应多个View)的情况难以处理,而Databinding则可以优雅的解决这一问题。
DataBinding基本功能模块与整体架构
功能模块
- compiler:APT模块,负责xml解析以及根据解析出的信息结合注解生成所需要的辅助文件。
- baseLibrary:向外提供DataBinding可用的注解
- library:提供可被观察的model
- adapters:封装view的属性改变方法,隔离VM与实际的View
整体架构

XML解析与APT
这一节主要介绍Databinding通过xml解析与APT的技术提取了哪些信息,并用这些信息生成了哪些文件,以及这些生成文件各自的作用。(*表示通配符)
*-layout.xml
文件地址:app/build/intermediates/data-binding/debug/layout-info/activity_data_binding-layout.xml
在看这个文件的内容之前,先看看我们编写的activity_data_binding.xml在编译前后的有什么不一样。
编译前:

编译后(反编译apk得到):

可以发现编译后的xml为原始xml的第二部分,需要注意的是这里所有的View都添加了属性android:tag。那第一部分的信息哪里去了呢?很自然的,可以知道,这部分信息被解析出来后存储到了最前面提到的*-layout.xml中,主要内容如下
这里的部分1与上图的1是一一对应的,除此之外,部分2也记录了View的tag信息,也就是说,我们声明的数据与View的绑定关系在这个生成文件中依然保存着。
接着看这几个标签,Variables与Imports和布局文件中的variable与import标签对应,Targets标签则告诉VM变化时对应的V(通过tag信息)、绑定关系是单向还是双向的以及DataBinding的表达式Expressions。
至于这个生成文件的具体解析生成过程可以参考compiler模块的这几个类 LayoutFileParser.java、LayoutXmlProcessor.java、ResourceBundle.java
在完成这个xml的生成之后,之后会利用APT根据*-layout.xml中保存的映射信息进一步生成下面要介绍的几个类,具体的实现过程参考源码compiler包中的代码。
BR.java
BR文件为每个model唯一分配了一个id,功能与R文件类似。
DataBinderMapperImpl.java
保存了上文提到的view tag与layout id的映射,也保存了布局文件和与之对应的ViewDataBinding的映射关系。
ActivityDataBindingBinding.java、ActivityDataBindingBindingImpl.java
真正建立与实现View与数据(双向)绑定的地方。具体的细节参考下一节的内容。
View与Model的绑定
Model
model可以是最简单的实体类。也可以是继承自Observable的,具有消息通知功能的被观察者类。
ViewModel
ViewModel在该框架中的实现,就是所有ViewDataBinding的具体实现,例如上文提到的ActivityDataBindingBindingImpl。当然我们也可以实现自己的ViewModel类,然后与ViewDataBinding组合。
view的绑定
绑定这一步的工作是在VM中建立View与Model之间的联系,使得Model与View能相互通信。
简单来说就是让VM持有View(可以更新View),并具有监听View变化的能力。
以Activity为例的话,第一次绑定的入口是DataBindingUtil.setContentView()。该方法最终会调用到DataBindingUtil.bind()。看一下这个方法的具体实现(简化代码):
1 | ("unchecked") |
继续跟进看sMapper.getDataBinder()方法的实现(简化代码):
1 |
|
继续跟进代码可以发现,最关键的实现在于ViewDataBinding.mapBindings()方法,该方法的作用是递归遍历View树,将真实的需要被绑定的View实例都取出来,然后再赋值给VM。
完成VM对View的持有之外,完全完成VM与View的绑定还需要VM监听View的状态变化,这部分代码在ActivityDataBindingBindingImpl.executeBindings()中:
1 | // ActivityDataBindingBindingImpl.class |
model的绑定
这个过程简单来说就是让VM持有Model(可以操作Model),并具有监听Model变化的能力。
以上面的例子为例,模型userInfo的初始化绑定,通过第一次调用ActivityDataBindingBindingImpl.setUserInfo()开始:
1 | // ActivityDataBindingBindingImpl.class |
核心的绑定过程在updateRegistration()方法中:
1 | // ViewDataBinding.class |
接着看registerTo()方法
1 | // ViewDataBinding.class |
listener.setTarget(observable);以及后面的方法不再给出,总之经过一系列调用后,会形成observable->listener->ViewDataBinding的持有链。换言之,observable可以通过该链路将变化通知到ViewDataBinding,并最终通知到View。
1 | protected void requestRebind() { |
最终真正实现绑定的地方是ActivityDataBindingBindingImpl.executeBindings()
1 | protected void executeBindings() { |
消息通知
这一节介绍绑定之后双向的消息发送的实现细节。
model变化通知到view
以改变上文提到的userInfo为例,调用入口为ActivityDataBindingBindingImpl.setUserInfo()
1 | public void setUserInfo(@Nullable com.xybean.mvpdemo.databinding.User UserInfo) { |
super.requestRebind()最终也会调用到ActivityDataBindingBindingImpl.executeBindings(),其过程与第一次数据绑定时几乎一样,区别在于不会执行以下逻辑
1 | if ((dirtyFlags & 0x2L) != 0) { |
显然这是监听View变化通知Model的监听器,因此只要在初始化的时候注册一次即可,具体的细节下一节再讨论。
view通知model变化
这里需要关注的类是android.databinding.InverseBindingListener,从名字上也可以看出来它的作用是反向绑定,即view通过这个监听器将变化发送到model。
同样以前面的UserInfo为例子,当我们改变了与之绑定的TextView的值的时候,由于TextView事先被注册了TextWatcher,具体见setTextWatcher.setTextWatcher(),而这个监听器最终会将Text的变化信息传递到InverseBindingListener中。
1 | // ActivityDataBindingBindingImpl.class |
同时也注意到,上面view改变后,只通知了数据实例改变值,却没有进一步通知其他绑定了该数据的view也同时刷新视图,对于这种情况,下一节展开讨论。
model变化自动驱动view改变
上面实现的绑定,想要model的改变通知到view,需要用户手动调用通知方法。如果想要完成自动驱动UI改变,则需要使用Observable系列的类封装model,以及使用@Bindable注解指示需要被监听的字段。
首先被@Bindable注解标记的字段会在BR文件中生成唯一的key,然后不论是用BaseObservable还是使用ObservableField的方式来包装被观察字段,最后的实现逻辑都是类似的,这里以BaseObservable为例。
监听绑定的过程上面介绍过了,这里就直接从BaseObservable的notifyPropertyChanged()开始,接下来会直接调用CallbackRegistry.notifyCallbacks(),然后会调用到PropertyChangeRegistry.onNotifyCallback(),并最终调用到WeakPropertyListener的onPropertyChanged()方法中。
1 | private void notifyCallbacks(T sender, int arg, A arg2, final int startIndex, |
最终的处理逻辑会回到ViewDataBinding.handleFieldChange()
1 | private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) { |
接着看requestRebind()的逻辑
1 | protected void requestRebind() { |
最终真正实现UI更新的地方是ActivityDataBindingBindingImpl.executeBindings()
1 | protected void executeBindings() { |
避免冗余的UI更新
根据上文的源码分析,可以发现,在这样一个场景下(View自己更新UI导致model更新,而model的更新又会导致View再更新一次),可能会导致UI的重复更新。
但是实际上这样的情况是不会发生的,原因就在于,Model改变后,我们更新UI不会直接调用View的原生方法传递model更新UI,而是通过一个Adapter,间接调用View的更新方法。以TextView为例:
1 | // 先看ActivityDataBindingBindingImpl.executeBindings() |
接下来看TextViewBindingAdapter.setText()的逻辑:
1 | ("android:text") |
可以发现,正是几次模型变化的校验,保证了内容不变时不刷新UI的逻辑,也就解决了重复刷新UI的问题。