DataBinding源码解析

DataBinding原理解析:双向绑定与MVVM实践
本文深入剖析了DataBinding的源码,揭示了其如何支持双向绑定、避免findViewById以及生成代码的机制。DataBinding通过自动生成的ActivityMainBinding.java、BR.java等文件,实现了VM与V之间的双向同步。在VM变化时,通过BaseObservable的监听机制通知View更新,反之亦然。同时,DataBinding通过绑定信息文件activity_main-layout.xml,简化了视图查找,提升了性能。

DataBinding是谷歌15年推出的library。DataBinding支持双向绑定,能大大减少绑定app逻辑与layout文件的“胶水代码”,例如setText、findViewById等代码。双向绑定,指的是将数据与界面绑定起来,当数据发生变化时会体现在界面上,反过来界面内容变化也会同步更新到数据上,使用DataBinding能轻松实现MVVM模式。本文在分析DataBinding时,将用ViewModel(VM)表示数据,用View(V)表示界面。

文初准备

本文着重于对DataBinding原理的分析,因此关于MVVM、DataBinding使用过程不做介绍。推荐一些相关的文章:

如何支持双向绑定?

DataBinding生成的代码介绍

在分析双向绑定的原理之前,先了解DataBinding生成的主要代码,有activity_main.xml、activity_main-layout.xml、ActivityMainBinding.java、BR.java、DataBinderMapper.java等等。

activity_main.xml

位置:
app/build/intermediates/data-binding-layout-out/debug/layout/activity_main.xml


<?xml version="1.0" encoding="utf-8"?>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent" android:tag="layout/activity_main_0" xmlns:android="http://schemas.android.com/apk/res/android">

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:tag="binding_1"     />

    </RelativeLayout>

可以看出,activity_main.xml与不用DataBinding前的布局文件差不多,唯一的区别是加了tag标记。 根布局的tag为layout/activity_main_0,而button的tag为binding_0。那我们使用DataBinding时的data标签以及里面的import与variable标签在哪里?其实它们都在下面介绍得activity_main-layout.xml里面。使用DataBinding重写后的布局文件无法被“理解”,因此需要经过DataBinding处理成正常的布局文件activity_main.xml与包含绑定信息的文件activity_main-layout.xml。

activity_main-layout.xml

位置:
app/build/intermediates/data-binding-info/debug/activity_main-layout.xml

<?xml version="1.0" encoding="utf-8"?>

<Layout layout="activity_main" modulePackage="com.example.databindingdemo" absoluteFilePath="/Users/linhaiyang807/Desktop/studioWorkspace/DataBindingDemo/app/src/main/res/layout/activity_main.xml" directory="layout" isMerge="false">
  <Variables declared="true" type="User" name="user">
    <location startLine="7" startOffset="8" endLine="9" endOffset="25"/>
  </Variables>
  <Imports type="com.example.databindingdemo.User" name="User">
    <location startLine="5" startOffset="8" endLine="5" endOffset="57"/>
  </Imports>CREATE_PROPERTY_LISTENER
  <Targets>
    <Target tag="layout/activity_main_0" view="RelativeLayout">
      <Expressions/>
      <location startLine="12" startOffset="4" endLine="22" endOffset="20"/>
    </Target>
    <Target id="@+id/button" tag="binding_1" view="Button">
      <Expressions>
        <Expression text="user.name" attribute="android:text">
          <Location startLine="20" startOffset="12" endLine="20" endOffset="38"/>
          <TwoWay>false</TwoWay>
          <ValueLocation startLine="20" startOffset="28" endLine="20" endOffset="36"/>
        </Expression>
      </Expressions>
      <location startLine="16" startOffset="8" endLine="20" endOffset="41"/>
    </Target>
  </Targets>
</Layout>

activity_main-layout.xml内包含了数据绑定相关的信息。Variables与Imports和布局文件中的variable与import标签对应,Targets标签则告诉VM变化时对应的V、绑定关系是单向还是双向的以及DataBinding的表达式Expressions。

ActivityMainBinding.java

位置:
app/build/generated/source/apt/debug/com/example/databindingdemo/databinding/ActivityMainBinding.java

public class ActivityMainBinding extends android.databinding.ViewDataBinding  {
   
   

    private static final android.databinding.ViewDataBinding.IncludedLayouts sIncludes;
    private static final android.util.SparseIntArray sViewsWithIds;
    static {
        sIncludes = null;
        sViewsWithIds = null;
    }
    // views
    public final android.widget.Button button;
    private final android.widget.RelativeLayout mboundView0;
    // variables
    private com.example.databindingdemo.User mUser;
    // values

    // …… 
}

可以看到ActivityMainBinding里面持有了一些view的变量,那是不是布局中所有的view都会在ActivityMainBinding中?其实不是,ActivityMainBinding里的view变量有三种类型,① 根布局 ② 含有@{}绑定的view ③ 含有id属性的view。 设置了id的view对应的变量为public的,可以通过ActivityMainBinding直接访问,避免了findViewById的过程,具体原理将在下面分析。ActivityMainBinding内部主要负责双向绑定的功能,留在下面分析。

BR

位置:
app/build/generated/source/apt/debug/com/android/databinding/library/baseAdapters/BR.java

public class BR {
   
   
    public static final int _all = 0;
    public static final int user = 1;
}

BR文件存储了绑VM的id,功能与R文件类似。

DataBinderMapper

位置:
app/build/generated/source/apt/debug/android/databinding/DataBinderMapper.java

class DataBinderMapper  {
    final static int TARGET_MIN_SDK = 21;
    public DataBinderMapper() {
    }
    public android.databinding.ViewDataBinding getDataBinder(android.databinding.DataBindingComponent bindingComponent, android.view.View view, int layoutId) {
        switch(layoutId) {
                case com.example.databindingdemo.R.layout.activity_main:
                    return com.example.databindingdemo.databinding.ActivityMainBinding.bind(view, bindingComponent);
        }
        return null;
    }
    // …… 
}

DataBinderMapper主要提供了从布局文件layoutId到ViewBinding类的映射。

初始化绑定

在初始化时我们会调用下面的代码:

final ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.setUser(user);

进入setUser:

public void setUser(User User) {
    this.mUser = User;
    synchronized(this) {
        this.mDirtyFlags |= 2L;
    }

    this.notifyPropertyChanged(1);
    super.requestRebind();
}

mDirtyFlags用于表示哪个属性发生

评论 11
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值