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实现参见源代码。
事件绑定、表达式,还没有具体深入使用后续继续跟进。