简介
前面的博客我已经说过了关于DataBinding的基本使用,这一章节主要介绍它的原理。
在编译阶段,DataBinding会介入,扫描所有 res/layout/ 下的所有布局文件,然后为其生成相应的 ViewBinding 抽象类和实现类。
源码分析
databinding的功能就是data和View的绑定。实现数据和视图额映射和同步。我们的View和ViewModel通过DataBinding可以实现单向绑定或双向绑定,做到UI和数据的相互监听。
映射
在xml中分别定义了数据变量和视图布局,在编译过程中,databinding会把xml分成两部分,数据部分如下:
(特别注意下这里的tag,后面会用到)
在app->build->intermediates->data_binding_layout_info_type_merge中生成的布局文件中,这里面就是记录所有节点的标签信息,比如名称代码的开始行结束行以及元素的tag等等信息。(如果一开始信息为一行可以可以使用快捷键 Windows : ctrl+Alt + L调整代码格式)
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Layout directory="layout" filePath="app\src\main\res\layout\activity_second.xml"
isBindingData="true" isMerge="false" layout="activity_second"
modulePackage="com.example.myapplication" rootNodeType="android.widget.LinearLayout">
<Variables name="user" declared="true" type="com.example.myapplication.User">
<location endLine="8" endOffset="51" startLine="6" startOffset="8" />
</Variables>
<Targets>
<Target tag="layout/activity_second_0" view="LinearLayout">
<Expressions />
<location endLine="28" endOffset="18" startLine="12" startOffset="4" />
</Target>
<Target id="@+id/idName" tag="binding_1" view="TextView">
<Expressions>
<Expression attribute="android:text" text="user.name">
<Location endLine="21" endOffset="38" startLine="21" startOffset="12" />
<TwoWay>false</TwoWay>
<ValueLocation endLine="21" endOffset="36" startLine="21" startOffset="28" />
</Expression>
</Expressions>
<location endLine="21" endOffset="40" startLine="17" startOffset="8" />
</Target>
<Target id="@+id/idGrade" tag="binding_2" view="TextView">
<Expressions>
<Expression attribute="android:text" text="user.grade">
<Location endLine="26" endOffset="39" startLine="26" startOffset="12" />
<TwoWay>false</TwoWay>
<ValueLocation endLine="26" endOffset="37" startLine="26" startOffset="28" />
</Expression>
</Expressions>
<location endLine="26" endOffset="41" startLine="22" startOffset="8" />
</Target>
</Targets>
</Layout>
布局部分的xml文件如下:
在app->build->intermediates->incremetal中生成的布局文件中
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".SecondActivity"
android:orientation="vertical"
android:tag="layout/activity_second_0"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<TextView
android:id="@+id/idName"
android:layout_width="match_parent"
android:layout_height="100dp"
android:tag="binding_1" />
<TextView
android:id="@+id/idGrade"
android:layout_width="match_parent"
android:layout_height="100dp"
android:tag="binding_2" />
</LinearLayout>
在上面生成的布局文件中相对自己编写的代码中的布局文件每个布局中的元素都增加了一个tag属性,并且删除了显示数据的属性(TextView中的text属性)
之后根据两个xml在编译时生成了ActivitySecondBinding和BR类,ActivitySecondBinding是根据xml的命名生成的,其实就是layout的ViewBinding类,它是通过APT/KAPT注解生成的抽象类,存放了View和Model的实例。BR跟R文件类似,主要作用是当数据改变后,可以用该标识符通知 DataBinding,用新的数据去更新UI。
ActivitySecondBinding 类存在于app/build/generated/data_binding_base_class_source_out/debug/out/包名/databinding/…
BR类存在于app/build/generated/source/kapt/debug/<包名>/BR.java
public abstract class ActivitySecondBinding extends ViewDataBinding {
@NonNull
public final TextView idGrade;
@NonNull
public final TextView idName;
@Bindable
protected User mUser;
protected ActivitySecondBinding(Object _bindingComponent, View _root, int _localFieldCount,
TextView idGrade, TextView idName) {
super(_bindingComponent, _root, _localFieldCount);
this.idGrade = idGrade;
this.idName = idName;
}
public abstract void setUser(@Nullable User user);
@Nullable
public User getUser() {
return mUser;
}
@NonNull
public static ActivitySecondBinding inflate(@NonNull LayoutInflater inflater,
@Nullable ViewGroup root, boolean attachToRoot) {
return inflate(inflater, root, attachToRoot, DataBindingUtil.getDefaultComponent());
}
...
@NonNull
@Deprecated
public static ActivitySecondBinding inflate(@NonNull LayoutInflater inflater,
@Nullable ViewGroup root, boolean attachToRoot, @Nullable Object component) {
return ViewDataBinding.<ActivitySecondBinding>inflateInternal(inflater, R.layout.activity_second, root, attachToRoot, component);
}
@NonNull
public static ActivitySecondBinding inflate(@NonNull LayoutInflater inflater) {
return inflate(inflater, DataBindingUtil.getDefaultComponent());
}
...
@NonNull
@Deprecated
public static ActivitySecondBinding inflate(@NonNull LayoutInflater inflater,
@Nullable Object component) {
return ViewDataBinding.<ActivitySecondBinding>inflateInternal(inflater, R.layout.activity_second, null, false, component);
}
public static ActivitySecondBinding bind(@NonNull View view) {
return bind(view, DataBindingUtil.getDefaultComponent());
}
...
@Deprecated
public static ActivitySecondBinding bind(@NonNull View view, @Nullable Object component) {
return (ActivitySecondBinding)bind(component, view, R.layout.activity_second);
}
}
生成的BR类如下:
package com.example.myapplication;
public class BR {
public static final int _all = 0;
public static final int user = 1;
}
而ActivitySecondBinding的实现类ActivitySecondBindingImpl通常在app/build/generated/source/kapt/debug/<包名>/databinding/…
关于实现类的代码下面会具体分析,实现类主要控制了Activity的绑定监听、执行刷新等
Databinding Apt/Kapt后产物
DataBinderMapper
DataBindingUtil类在被加载时(在 Activity.onCreate 中调用),根据成员变量的生命周期,会先初始这个 mapper。
public class DataBindingUtil {
private static DataBinderMapper sMapper = new DataBinderMapperImpl();
private static DataBindingComponent sDefaultComponent = null;
}
该类也是在编译阶段产生的,通常是他的实现类DataBinderMapperImpl
DataBinderMapperImpl存在于app/build/generated/source/kapt/debug/androidx/databinding/DataBinderMapperImpl.java
public class DataBinderMapperImpl extends MergedDataBinderMapper {
DataBinderMapperImpl() { //调用了父类的addMapper方法
//注意这里添加的并不是自身,而是下面要说的另一个同名的实现类
addMapper(new com.example.myapplication.DataBinderMapperImpl());
}
}
//---------MergedDataBinderMapper.addMapper---------------------------
public void addMapper(DataBinderMapper mapper) {
Class<? extends DataBinderMapper> mapperClass = mapper.getClass();
// 如果之前不存在,则加入 mExistingMappers
if (mExistingMappers.add(mapperClass)) {
mMappers.add(mapper);
//获取该 mapper 所依赖的其它 DataBinderMapper,具体实现在下面代码中
final List<DataBinderMapper> dependencies = mapper.collectDependencies();
for(DataBinderMapper dependency : dependencies) {
//将依赖的其他DataBinderMapper也添加进去
addMapper(dependency);
}
}
}
所以DataBinderMapper的实现类只是将生成的DataBinderMapperImpl(如果之前没有保存的话)以及它所依赖的其他DataBinderMapper加入到MergedDataBinderMapper的mExistingMappers和mMappers集合中。
DataBinderMapperImpl
有另一个DataBinderMapper的实现类,虽然和上面实现类的类名相同,但是其中的实现并不相同。
这个类位于app/build/generated/source/kapt/debug/<包名>/DataBinderMapperImpl.java
这个类重写了父类的collectDependencies()方法。
public List<DataBinderMapper> collectDependencies() {
ArrayList<DataBinderMapper> result = new ArrayList<DataBinderMapper>(1);
result.add(new androidx.databinding.library.baseAdapters.DataBinderMapperImpl());
return result;
}
DataBinding的启动流程
DataBindingUtil.setContentView(this,R.layout.activity_main);方法作为入口,发现最后还是调用activity的setContentView()方法。
//可以通过DataBindingUtil的setDefaultComponent方法来进行赋值
private static DataBindingComponent sDefaultComponent = null;
public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity,
int layoutId) {
return setContentView(activity, layoutId, sDefaultComponent);
}
---
public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity,
int layoutId, @Nullable DataBindingComponent bindingComponent) {
activity.setContentView(layoutId); //其实还是调用了activity的setContentView方法
/*DecorView包含的子孙Views(DecorView的父View是PhoneWindow,
它是 Window 的唯一子类,再往上就是 Activity 了) */
View decorView = activity.getWindow().getDecorView();
ViewGroup contentView = (ViewGroup) decorView.findViewById(android.R.id.content);
return bindToAddedViews(bindingComponent, contentView, 0, layoutId);
}
------
private static <T extends ViewDataBinding> T bindToAddedViews(DataBindingComponent component,
ViewGroup parent,// contentView,一般只有一个 child 且是 ViewGroup
int startChildren,
int layoutId//layoutId = R.layout.xxxx,项目中的 layout xml
) {
final int endChildren = parent.getChildCount();
final int childrenAdded = endChildren - startChildren;
if (childrenAdded == 1) { //如果仅有一个子View
final View childView = parent.getChildAt(endChildren - 1);
return bind(component, childView, layoutId);
} else { //如果有多个子View
final View[] children = new View[childrenAdded];
for (int i = 0; i < childrenAdded; i++) {
children[i] = parent.getChildAt(i + startChildren);
}
return bind(component, children, layoutId);
}
}
调用了DataBindingUtil的bind方法
public class DataBindingUtil {
private static DataBinderMapper sMapper = new DataBinderMapperImpl();
//对应上面有多个子View的情况
static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View[] roots,
int layoutId) {
return (T) sMapper.getDataBinder(bindingComponent, roots, layoutId);
}
//一般情况下是这种,只有一个子View,并且是ViewGroup
static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View root,
int layoutId) {
/* 通过 DataBinderMapper 来查找具体的 XxxViewBinding,sMapper 中有两个 mapper:
[0]: 是 DataBinding APT 为我们生成的 DataBinderMapperImpl
[1]: 是 DataBinding Lib 中 androidx.databinding下的 DataBinderMapperImpl
这里使用的是第二种情况,在一开始创建了DataBinderMapperImpl对象,
第二种情况的实现类和当前DataBindingUtil在同一个包下*/
return (T) sMapper.getDataBinder(bindingComponent, root, layoutId);
}
}
这里最终调用的是DataBinderMapperImpl的getDataBinder方法,但是DataBinderMapperImpl并没有重写这个方法,但是他的父类MergedDataBinderMapper中重写了这个方法,所以其实调用的是MergedDataBinderMapper中方法
@Override
public ViewDataBinding getDataBinder(DataBindingComponent bindingComponent, View view,
int layoutId) {
for(DataBinderMapper mapper : mMappers) {
ViewDataBinding result = mapper.getDataBinder(bindingComponent, view, layoutId);
if (result != null) {
return result;
}
}
if (loadFeatures()) {
return getDataBinder(bindingComponent, view, layoutId);
}
return null;
}
这里的mMappers中存放就是APT 为我们生成的 DataBinderMapperImpl了,然后再次调用getDataBinder
public class DataBinderMapperImpl extends DataBinderMapper {
private static final int LAYOUT_ACTIVITYSECOND = 1;
private static final int LAYOUT_FRAGMENTMY = 2;
private static final SparseIntArray INTERNAL_LAYOUT_ID_LOOKUP = new SparseIntArray(2);//主要针对Int->Int键值对
static { //添加数据,将定义的databinding的布局文件和类定义的常量对应
INTERNAL_LAYOUT_ID_LOOKUP.put(com.example.myapplication.R.layout.activity_second, LAYOUT_ACTIVITYSECOND);
INTERNAL_LAYOUT_ID_LOOKUP.put(com.example.myapplication.R.layout.fragment_my, LAYOUT_FRAGMENTMY);
}
@Override
public ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) {
int localizedLayoutId = INTERNAL_LAYOUT_ID_LOOKUP.get(layoutId);
if(localizedLayoutId > 0) {
//这里的tag标签在上面生成的布局文件中已经定义了
//获取根视图的 tag: layout/activity_xxx_数字(如:layout/activity_second_0)
final Object tag = view.getTag();
if(tag == null) {
throw new RuntimeException("view must have a tag");
}
switch(localizedLayoutId) {
case LAYOUT_ACTIVITYSECOND: {
if ("layout/activity_second_0".equals(tag)) {
return new ActivitySecondBindingImpl(component, view);
}
throw new IllegalArgumentException("The tag for activity_second is invalid. Received: " + tag);
}
case LAYOUT_FRAGMENTMY: {
if ("layout/fragment_my_0".equals(tag)) {
return new FragmentMyBindingImpl(component, view);
}
throw new IllegalArgumentException("The tag for fragment_my is invalid. Received: " + tag);
}
}
}
return null;
}
}
最后返回了编译期间生成的对应的ViewBinding的实现类,就是在编译阶段根据R.layout.XXX生成了新的带有tag标签的布局文件,并通过tag获得相应的ViewDataBinding类对象。
具体看一下相应的ViewDataBinding类的构造函数
public ActivitySecondBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
// 重点看 mapBindings 方法
// root = 根视图( tag = layout/activity_second_0 )
// 数字3就是这个就是中间产物 activity_second-layout.xml 中的 Targets 个数
this(bindingComponent, root, mapBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds));
}
//----------------------ViewDataBinding.mapBindings----------------------
protected static Object[] mapBindings(DataBindingComponent bindingComponent, View root,
int numBindings, IncludedLayouts includes, SparseIntArray viewsWithIds) {
Object[] bindings = new Object[numBindings];
// 从我们的根视图开始,递归遍历找出所有含有 binding_数字 的控件,赋值到 bindings 中
mapBindings(bindingComponent, root, bindings, includes, viewsWithIds, true);
return bindings;
}
private static void mapBindings(DataBindingComponent bindingComponent, View view,
Object[] bindings, IncludedLayouts includes, SparseIntArray viewsWithIds,
boolean isRoot) {
final int indexInIncludes;
final ViewDataBinding existingBinding = getBinding(view);
if (existingBinding != null) {
return;
}
Object objTag = view.getTag();
final String tag = (objTag instanceof String) ? (String) objTag : null;
boolean isBound = false;
if (isRoot && tag != null && tag.startsWith("layout")) {
// 判断是否是根布局,且 tag 以 layout 开头
// 根布局的 tag = layout/activity_xxx_数字
//在bindings数组中,以tag后的数字为下标,将对应tag的View放进数组指定下标中
......
} else if (tag != null && tag.startsWith(BINDING_TAG_PREFIX)) {
// 判断 tag 是否以 binding_ 开头
// 绑定的控件的 tag = binding_数字
//在bindings数组中,以tag后的数字为下标,将对应tag的View放进数组指定下标中
......
} else {
// Not a bound view
indexInIncludes = -1;
}
......
if (view instanceof ViewGroup) {
final ViewGroup viewGroup = (ViewGroup) view;
final int count = viewGroup.getChildCount();
for (int i = 0; i < count; i++) {
// 遍历 ViewGroup 的 children
.......
if (!isInclude) {
// 递归遍历子view
mapBindings(bindingComponent, child, bindings, includes, viewsWithIds, false);
}
}
}
}
}
-------------------------------总结-----------------------------------
//所以mapBindings返回的就是bindings数组,数组中存放的就是布局文件中的View
//以生成的tag中的数字作为View在bindings数组中对应的下标
private ActivitySecondBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
//调用了ActivitySecondBinding的构造函数
super(bindingComponent, root, 2
, (android.widget.TextView) bindings[2]
, (android.widget.TextView) bindings[1]
);
//将tag全部置空
this.idGrade.setTag(null);
this.idName.setTag(null);
this.mboundView0 = (android.widget.LinearLayout) bindings[0];
this.mboundView0.setTag(null);
//设置根布局
setRootTag(root);
// listeners
invalidateAll();
}
在ActivitySecondBindingImpl构造函数中,调用了父类的构造函数,也就是ActivitySecondBinding的构造函数。
public abstract class ActivitySecondBinding extends ViewDataBinding {
@NonNull
public final TextView idGrade;
@NonNull
public final TextView idName;
@Bindable
protected User mUser;
protected ActivitySecondBinding(Object _bindingComponent, View _root, int _localFieldCount,
TextView idGrade, TextView idName) {
//又调用了ViewDataBinding的构造方法
super(_bindingComponent, _root, _localFieldCount);
this.idGrade = idGrade;
this.idName = idName;
}
}
到这里就是将刚才分析的数组中的View赋值给了ActivitySecondBinding中的属性View,DataBinding也就持有了view。
在ActivitySecondBinding的构造函数中再次调用了父类的构造方法。也就是ViewDataBinding的构造方法。
protected ViewDataBinding(Object bindingComponent, View root, int localFieldCount) {
//checkAndCastToBindingComponent就是返回一个DataBindingComponent对象或者null
this(checkAndCastToBindingComponent(bindingComponent), root, localFieldCount);
}
----
protected ViewDataBinding(DataBindingComponent bindingComponent, View root, int localFieldCount) {
mBindingComponent = bindingComponent;
mLocalFieldObservers = new WeakListener[localFieldCount];
this.mRoot = root;
if (Looper.myLooper() == null) {
throw new IllegalStateException("DataBinding must be created in view's UI Thread");
}
if (USE_CHOREOGRAPHER) {
//如果设备系统 SDK 版本不低于 API-16
//进行了mChoreographer初始化以及mFrameCallback的具体实现
mChoreographer = Choreographer.getInstance();
mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
mRebindRunnable.run();
}
};
} else {
//绑定至主线程的Handler
mFrameCallback = null;
mUIThreadHandler = new Handler(Looper.myLooper());
}
}
根据上面的构造函数已经有mChoreographer和mFrameCallback或者mUIThreadHandler的初始化操作。
再继续往后看ActivitySecondBindingImpl构造函数的代码:
public void invalidateAll() {
synchronized(this) {
mDirtyFlags = 0x8L;
}
requestRebind();
}
protected void requestRebind() {
if (mContainingBinding != null) {
mContainingBinding.requestRebind();
} else {
final LifecycleOwner owner = this.mLifecycleOwner;
if (owner != null) {
Lifecycle.State state = owner.getLifecycle().getCurrentState();
if (!state.isAtLeast(Lifecycle.State.STARTED)) {
return; // wait until lifecycle owner is started
}
}
synchronized (this) {
if (mPendingRebind) {
return;
}
mPendingRebind = true;
}
//如果设备系统 SDK 版本不低于 API-16,即 SDK 4.1+,就用 mChoreographer 来渲染
if (USE_CHOREOGRAPHER) {
//mChoreographer 可以用来监控帧率(fps,也就是每秒显示的帧数)
//在上面的ViewDataBinding构造函数中mFrameCallback最终也是调用了mRebindRunnable
mChoreographer.postFrameCallback(mFrameCallback);
} else {
//使用 UI主线程的 Handler 来运行 Runnable
mUIThreadHandler.post(mRebindRunnable);
}
//最后都会执行mRebindRunnable
}
}
最终调用都是mRebindRunnable.run看一下具体实现
private final Runnable mRebindRunnable = new Runnable() {
@Override
public void run() {
synchronized (this) {
mPendingRebind = false;
}
processReferenceQueue();
...
//最终都会调用这个方法
executePendingBindings();
}
};
----
public void executePendingBindings() {
//由requestRebind()可以判断出mContainingBinding == null
if (mContainingBinding == null) {
executeBindingsInternal();
} else {
mContainingBinding.executePendingBindings();
}
}
private void executeBindingsInternal() {
if (mIsExecutingPendingBindings) {
requestRebind();
return;
}
if (!hasPendingBindings()) {
return;
}
mIsExecutingPendingBindings = true;
mRebindHalted = false;
...
if (!mRebindHalted) {
//这里调用的还是ActivitySecondBindingImpl的executeBindings()
executeBindings();
if (mRebindCallbacks != null) {
mRebindCallbacks.notifyCallbacks(this, REBOUND, null);
}
}
mIsExecutingPendingBindings = false;
}
最后调用的是ActivitySecondBindingImpl的executeBindings()
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
androidx.databinding.ObservableField<java.lang.String> userName = null;
com.example.myapplication.User user = mUser;
androidx.databinding.ObservableInt userGrade = null;
java.lang.String userNameGet = null;
int userGradeGet = 0;
if ((dirtyFlags & 0xfL) != 0) {
if ((dirtyFlags & 0xdL) != 0) {
if (user != null) {
// read user.name
userName = user.getName();
}
updateRegistration(0, userName);
if (userName != null) {
// read user.name.get()
userNameGet = userName.get();
}
}
if ((dirtyFlags & 0xeL) != 0) {
if (user != null) {
// read user.grade
userGrade = user.getGrade();
}
updateRegistration(1, userGrade);
if (userGrade != null) {
// read user.grade.get()
userGradeGet = userGrade.get();
}
}
}
// 根据不同标志位(编译时动态决定),刷新 UI 控件
if ((dirtyFlags & 0xeL) != 0) {
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.idGrade, stringValueOfUserGrade);
}
if ((dirtyFlags & 0xdL) != 0) {
//在这里将数据进行了赋值,根据@+id中定义的id名定义控件
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.idName, userNameGet);
}
}
--
public static void setText(TextView view, CharSequence text) {
final CharSequence oldText = view.getText();
if (text == oldText || (text == null && oldText.length() == 0)) {
return;
}
if (text instanceof Spanned) {
if (text.equals(oldText)) {
return; // No change in the spans, so don't set anything.
}
} else if (!haveContentsChanged(text, oldText)) {
return; // No content changes, so don't set anything.
}
//使用了TextView的setText
view.setText(text);
}
总结
- 使用Databinding在编译过程中会生成带有tag的布局文件
- 在DataBindingUtil的setContentView方法中根据编译期间新生成的布局文件的tag得到相应布局的ViewBinding的实现类
- 将布局的子View,全部存放在数组中,然后将数组中的View赋值给生成的ViewBinding的属性,这样就可以直接使用生成的ViewBinding的属性来使用view,不用每次都调用findViewByid了
- 最后根据不同标志位(编译时动态决定),刷新 UI 控件。
自动刷新
//mDirtyFlags是一个刷新标记,用于判断数据的变化对应刷新哪些view
//flag mapping是每个刷新项的映射标记,所有可变化部分都将在这里展示
private long mDirtyFlags = 0xffffffffffffffffL;
/* flag mapping
flag 0 (0x1L): user.name
flag 1 (0x2L): user.grade
flag 2 (0x3L): user
flag 3 (0x4L): null
flag mapping end*/
//end
在ActivitySecondBindingImpl中的mDirtyFlags标记位非常重要,如果使用了ObservableField等用于自动刷新的类,则在标记位就会包含相应的映射,如果使用的是String、Int等数据类型,则不会包含,上面的代码是下面定义的User编译期间生成的,所以flag mapping中也会包含name和grade的信息。
class User(val name:ObservableField<String>,val grade :ObservableInt{
}
如果将User改为这种
class User(val name:String,val grade :Int){
}
则编译产生的ViewBinding的实现类的mDirtyFlags的映射也会改变:
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
java.lang.String userName = null;
com.example.myapplication.User user = mUser;
java.lang.String stringValueOfUserGrade = null;
int userGrade = 0;
if ((dirtyFlags & 0x3L) != 0) {
if (user != null) {
// read user.name
userName = user.getName();
// read user.grade
userGrade = user.getGrade();
}
// read String.valueOf(user.grade)
stringValueOfUserGrade = java.lang.String.valueOf(userGrade);
}
// batch finished
if ((dirtyFlags & 0x3L) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.idGrade, stringValueOfUserGrade);
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.idName, userName);
}
}
private long mDirtyFlags = 0xffffffffffffffffL;
/* flag mapping
flag 0 (0x1L): user
flag 1 (0x2L): null
flag mapping end*/
其实对比了前两种情况,除了标志位的变化之后还有在进行属性赋值的时候,如果是ObservableField类型的数据都会将属性值作为参数去调用updateRegistration()方法
private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
@Override
public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();
}
};
//updateRegistration(0, userName);
protected boolean updateRegistration(int localFieldId, Observable observable) {
return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
}
------
private boolean updateRegistration(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
if (observable == null) {
return unregisterFrom(localFieldId);
}
//mLocalFieldObservers进行添加都是在registerTo中进行操作的
WeakListener listener = mLocalFieldObservers[localFieldId];
if (listener == null) {
registerTo(localFieldId, observable, listenerCreator);
return true;
}
if (listener.getTarget() == observable) {
return false;//nothing to do, same object
}
unregisterFrom(localFieldId);
registerTo(localFieldId, observable, listenerCreator);
return true;
}
最后调用的还是registerTo方法,看一下具体实现:
protected void registerTo(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
if (observable == null) {
return;
}
WeakListener listener = mLocalFieldObservers[localFieldId];
if (listener == null) {
//根据上面的代码可以知道listenerCreator.create会创建一个WeakListener对象
//通过构造方法创建WeakPropertyListener对象,又通过getListener返回WeakListener对象
listener = listenerCreator.create(this, localFieldId);
mLocalFieldObservers[localFieldId] = listener;
if (mLifecycleOwner != null) {
listener.setLifecycleOwner(mLifecycleOwner);
}
}
listener.setTarget(observable);
}
重点看setTarget()
public void setTarget(T object) {
unregister();
mTarget = object;//mTarget就是ObservableField对象
if (mTarget != null) {
//mObservable是在使用构造函数的时候进行创建的代码如下,所以实际上是一个WeakPropertyListener类型
//mListener = new WeakListener<Observable>(binder, localFieldId, this);
mObservable.addListener(mTarget);
}
}
----
public void addListener(Observable target) {
target.addOnPropertyChangedCallback(this);
}
---
我们知道addListener()方法中的target就是ViewModel中的ObservableField对象,它是一个被观察者。通过addOnPropertyChangedCallback()方法,相当于向target注册一个观察者
public interface Observable {
void addOnPropertyChangedCallback(OnPropertyChangedCallback callback);
void removeOnPropertyChangedCallback(OnPropertyChangedCallback callback);
abstract class OnPropertyChangedCallback {
public abstract void onPropertyChanged(Observable sender, int propertyId);
}
}
在WeakPropertyListener的具体实现:
public void onPropertyChanged(Observable sender, int propertyId) {
ViewDataBinding binder = mListener.getBinder();
if (binder == null) {
return;
}
Observable obj = mListener.getTarget();
if (obj != sender) {
return; // notification from the wrong object?
}
binder.handleFieldChange(mListener.mLocalFieldId, sender, propertyId);
}
-----
private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
if (mInLiveDataRegisterObserver) {
//我们使用的是LiveData注册,它总是导致字段更改
//我们可以忽略。无论如何,该值都将在之后立即读取
return;
}
//判断数据是否发生了更改
boolean result = onFieldChange(mLocalFieldId, object, fieldId);
if (result) {
requestRebind();//与前面过程相同,最终更新数据。
}
}
在onFieldChange中是判断是否数据发生了更改,看一下在ActivitySecondBindingImpl中的具体实现:
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
switch (localFieldId) {
case 0 :
return onChangeUserName((androidx.databinding.ObservableField<java.lang.String>) object, fieldId);
case 1 :
return onChangeUserGrade((androidx.databinding.ObservableInt) object, fieldId);
}
return false;
}
以其中onChangeUserName为例:
private boolean onChangeUserName(androidx.databinding.ObservableField<java.lang.String> UserName, int fieldId) {
if (fieldId == BR._all) {
synchronized(this) {
//根据前面的标志位映射的注释可知,这个方法用来判断user.name是否发生了变化
//flag 0 (0x1L): user.name
mDirtyFlags |= 0x1L;
}
return true;
}
return false;
}
在数据源有变化的时候,回调onPropertyChanged()方法。在onPropertyChanged()方法中,又通过WeakListener对象获取ViewDataBinding对象,之后再通过ViewDataBinding对象的handleFieldChange()方法去更新View。
总结
对于自动刷新过程中,updateRegistration()所做的事情就是:
对View的显示赋值进行修改之前,给每一个ObservableField注册一个观察者WeakPropertyListener,当数据发生变化的时候就会调用requestRebind()来更新数据,
通过以上分析,我们可以知道其实ViewModel持有WeakPropertyListener,WeakPropertyListener又持有WeakListener,而WeakListener又持有ViewDataBinding对象(弱引用方式),也就是ViewModel间接地持有了ViewDataBinding。
到这,DataBinding的启动部分分析完成!
设置数据
给布局文件设置数据
class SecondActivity : AppCompatActivity() {
private lateinit var binding :ActivitySecondBinding
private lateinit var user :User
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this,R.layout.activity_second)
user = User(ObservableField("张三"), ObservableInt(101))
binding.user = user
}
分析了上面的代码已经知道,binding.user调用的实际上是ActivitySecondBindingImpl类的setUser方法
public void setUser(@Nullable com.example.myapplication.User User) {
//这里的mUser准确的说是父类ActivitySecondBinding的属性
this.mUser = User; //意味这Binding持有了ViewModel
synchronized(this) {
mDirtyFlags |= 0x4L;
}
notifyPropertyChanged(BR.user);
super.requestRebind();
}
给类里面的mUser赋值,再调用个notifyPropertyChanged()方法。notifyPropertyChanged()其实就是刷新UI的方法,这个后面讨论。requestRebuild(),前面已经分析过了就是根据不同的标志位来刷新UI的。
反向绑定
因为EditText继承TextView,我们拿常用的EditText的text属性结合TextViewBindingAdapter源码来说明反向绑定的过程。
# TextViewBindingAdapter
① //event可缺省
@InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged")
public static String getTextString(TextView view) {
return view.getText().toString();
}
②
@BindingAdapter(value = { ..., "android:textAttrChanged"}, requireAll = false)
public static void setTextWatcher(TextView view, ...,
final InverseBindingListener textAttrChanged) {
final TextWatcher newValue;
...
newValue = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
...
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
...
//收到事件 通知框架发生变化
if (textAttrChanged != null) {
textAttrChanged.onChange();
}
}
@Override
public void afterTextChanged(Editable s) {
...
}
};
...
if (newValue != null) {
view.addTextChangedListener(newValue);
}
}
对于EditText来说可驱动数据变化的事件显然就是TextChangedListener,每当输入字符变化时会回调onTextChanged方法,dataBinding框架使用了一个InverseBindingListener接口以供用户将事件通知抛出。与InverseBindingListener接口对应需要向框架提供一个绑定属性textAttrChanged,默认它是由属性名+AttrChanged后缀组成(上面②处代码)。InverseBindingListener接口具体实现代码由框架生成,总得来说就是获取控件属性的当前值,然后用此值更新数据。
避免无限循环
通过图例我们可以发现,如果事件驱动反向绑定成功后,数据会发生变化,按正常逻辑来讲,将继续触发单向绑定,如此一来将陷入无限循环中。
为中断这种循环,通常的做法是在更新UI前,校验新旧数据是否相同,如果相同则不进行刷新动作。比如TextView,其内部的setText方法并不会检验新旧数据的一致性问题,所以在TextViewBindingAdapter内重新绑定了android:text属性,添加校验逻辑。
@BindingAdapter("android:text")
public static void setText(TextView view, CharSequence text) {
//校验
final CharSequence oldText = view.getText();
if (text == oldText || (text == null && oldText.length() == 0)) {
return;
}
...
view.setText(text);
}
中间调用 updateRegistration方法
private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
@Override
public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();
}
};
protected boolean updateRegistration(int localFieldId, Observable observable) {
return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
}
----
private boolean updateRegistration(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
if (observable == null) {
return unregisterFrom(localFieldId);
}
WeakListener listener = mLocalFieldObservers[localFieldId];
if (listener == null) {
registerTo(localFieldId, observable, listenerCreator);
return true;
}
if (listener.getTarget() == observable) {
return false;//nothing to do, same object
}
unregisterFrom(localFieldId);
registerTo(localFieldId, observable, listenerCreator);
return true;
}
---
protected void registerTo(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
if (observable == null) {
return;
}
WeakListener listener = mLocalFieldObservers[localFieldId];
if (listener == null) {
listener = listenerCreator.create(this, localFieldId);
mLocalFieldObservers[localFieldId] = listener;
if (mLifecycleOwner != null) {
listener.setLifecycleOwner(mLifecycleOwner);
}
}
listener.setTarget(observable);
}