Android Data Binding代码分析

Android团队推出Data Binding支持已经一年有余,但碍于项目已经在使用原始setXXX的方式,想要完全替换需要一定时间,一直没有进入todo list。最近在一个小demo中试验了一下。详细的开发示例不再赘述,可以参考Android Dev官网的代码示例,中文版可以参考简书的一篇比较新鲜的译文

这里主要分析一下Data Binding的生成代码和binding过程分析,请确保Support Repository(39)、Support Library(23.2.1)、Gradle plugin(2.2.2)、Gradle(2.14.1)、Android Studio(2.2.2)更新到最新版本。

Data Binding的代码生成由Android Gradle插件根据Layout XML来生成。

示例XML activity_bind.xml

<?xml version="1.0" encoding="utf-8"?>
   <layout xmlns:android="http://schemas.android.com/apk/res/android">
      <data>
          <variable name="user" type="com.example.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>

对应生成代码结构

package:android.databinding

DataBinderMapper.java     //实现Layout的映射

InnerBrLookup---存储BR文件中成员变量名与索引的对应关系

convertBrIdToString()---根据BR文件成员变量索引转换出model成员变量名

getLayoutId()---根据Data binding的Layout tag转换Layout Resource ID

getDataBinder()---执行bind并返回对应的binding对象

DataBindingComponent.java

DynamicUtil.java                 //数值转换、版本相关特殊处理工具类

package:com.android.databinding.library.baseAdapters

BR.java                             //类似于R文件,data层model类的int映射

package:apkPackageName.binding

ActivityBindBinding.java        //Data Bind的关键的关键,继承自ViewDataBinding,与XML文件名保持一致

bind()---activity对View的写入和获取,在验证XML后,最终通过ActivityBindBinding的构造方法获取到对应的绑定View。

inflate()---与bind()类似,通过LayoutInflater获取到View进行bind()

executeBindings()---执行model层的数据写入,写入是通过一系列adpter根据android:xxx标签的实现方法一一对应写入,具体的adapter参见后续。

BR.java                            //类似于R文件,data层model类的int映射

对应打包文件

<?xml version="1.0" encoding="utf-8"?>
   <LinearLayout
       android:layout_height="fill_parent"
       android:layout_width="fill_parent"
       android:orientation="vertical"
       android:tag="layout/activity_bind_0"
       xmlns:android="http://schemas.android.com/apk/res/android">
       <TextView
           android:layout_height="wrap_content"
           android:layout_width="wrap_content"
           android:tag="binding_1" />
       <TextView
           android:layout_height="wrap_content"
           android:layout_width="wrap_content"
           android:tag="binding_2" />
   </LinearLayout>

通过打包后的文件查看,我们可以发现和没有使用data binding的XML基本没有区别,只有两点不太一样:

1、每个需要binding的View上多了一个tag标签;

其实在data binding的过程中android:tag标签发挥了重大作用,所有的待binding View都是通过tag来交互的,而且tag的名字有固定格式:root View的tag是"layout/layoutxmlname__0",其他View的tag是binding_1,2,3.....,格式可以参考[android.databinding.ViewDataBinding.mapBindings()_](https://android.googlesource.com/platform/frameworks/data-binding/+/master/extensions/library/src/main/java/android/databinding/ViewDataBinding.java)方法,这里有详细的解析过程。

可能我们还会有另外一个疑问,如果本身View需要设置tag,那岂不是会被覆盖,Android团队考虑到这一点,在生成binding的代码中有对XML中手动设置tag的处理操作:binding的XML处理规则不变,binding后通过硬编码的方式调用View的setTag()再写入一次,这样就保证了binding规则不变,tag同样能够生效。

2、设置的setText属性不见了踪影。

setText虽然在XML中消失了,但它的设置被转移到了Binding的过程中,就是上面提到的executeBindings()中。当前Android官方给出的data binding库中支持37个组件,基本覆盖了常用的组件库,对这些组件的属性绑定支持是通过一系列的adapter来实现的,比如TextView对应的adapter是android.databinding.adapters.TextViewBindingAdapter。在每一个adapter中通过注解实现了各自组件的很多属性,这样通过XML解析到需要binding的属性,直接生成相应的方法调用,比如android:text<==>textView.setText()。其他组件的adapter实现参见源代码

事件绑定、表达式,还没有具体深入使用后续继续跟进。

android-data-binding