DataBinding 内部隐藏的秘密

在开发中经常用到 DataBinding, 但自己对这一块的了解仅限于用法, 并未深入探究其内部工作原理, 导致踩了许多坑。便花时间研究了下 DataBinding 中核心的源码, 并撰写此文, 以帮助后来的读者能够通过本文快速了解 DataBinding 内部的一些机制。

1 用法回顾

编写继承自 BaseObservable 的 ViewModel:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ViewModel extends BaseObservable {

@Bindable
private String text;

public String getText() {
return text;
}

public void setText(String text) {
this.text = text;
notifyPropertyChanged(BR.text);
}

}

界面布局中书写表达式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<layout>

<data>
<variable name="vm" type="top.wuhaojie.databindingdemo.ViewModel" />
</data>

<android.support.constraint.ConstraintLayout>

<TextView
android:id="@+id/tv"
size="@{100}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{vm.text}"/>


<EditText
android:id="@+id/et"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@={vm.text}"/>


</android.support.constraint.ConstraintLayout>

</layout>

主逻辑中:

1
2
3
4
binding = DataBindingUtil.inflate(getLayoutInflater(), R.layout.activity_main, null, false);
binding.setVm(viewModel);
// 数据变化
viewModel.setText("hello");

2 绑定 View 过程

2.1 编译期布局文件处理

DataBinding 带表达式的布局文件不能直接被 Android 处理,需要在编译期处理处理成普通的 Layout 布局:

原始的布局文件

image-20181117182536793

生成后的布局文件

image-20181117182623391

可以看到增加了 Tag 标签用来唯一标识 View,并且去除了表达式。

同时生成了原始文件的描述信息

image-20181117182643496

2.2 寻找 ViewDataBinding

DataBinding 用法中第一步就是调用 DataBindingUtil.inflate 方法,其对 inflate 后的 View 进行 bind,然后返回对应的 ViewDataBinding,通过寻找 Mapper 中的方法实现:

1
2
3
4
static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View root,
int layoutId)
{

return (T) sMapper.getDataBinder(bindingComponent, root, layoutId);
}

sMapper 是个静态变量,在类开始初始化:

1
private static DataBinderMapper sMapper = new DataBinderMapperImpl();

而 DataBinderMapperImpl 是 在编译期生成的 Java 类,看看 getDataBinder() 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public ViewDataBinding getDataBinder(DataBindingComponent bindingComponent, View view, int layoutId) {
switch (layoutId) {
case R.layout.activity_main: {
final Object tag = view.getTag();
if (tag == null) throw new RuntimeException("view must have a tag");
if ("layout/activity_main_0".equals(tag)) {
return new ActivityMainBinding(bindingComponent, view);
}
throw new IllegalArgumentException("The tag for activity_main is invalid. Received: " + tag);
}
}
return null;
}

Tag 标签是在布局文件处理中生成的,由两个部分组成:前缀 + 下标序号。

2.3 更高效的 View 赋值

mapBindings 方法通过 RootView 来 find 所有的 View,返回到一个 View 数组:

1
2
3
4
5
6
protected static Object[] mapBindings(DataBindingComponent bindingComponent, View root,
int numBindings, IncludedLayouts includes, SparseIntArray viewsWithIds) {
Object[] bindings = new Object[numBindings];
mapBindings(bindingComponent, root, bindings, includes, viewsWithIds, true);
return bindings;
}

进一步跟进实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
private static void mapBindings(..., View view, Object[] bindings, ... ) {
...
if (isRoot && tag != null && tag.startsWith("layout")) {
// 根布局
...
if (bindings[index] == null) {
bindings[index] = view;
}
} else if (tag != null && tag.startsWith("binding_")) {
// 普通 View
int tagIndex = parseTagInt(tag, BINDING_NUMBER_START); // 从 Tag 中取出数值即为数组下标
if (bindings[tagIndex] == null) {
bindings[tagIndex] = view;
}
...
} else {
// Not a bound view
indexInIncludes = -1;
}
...
if (view instanceof ViewGroup) {
// ViewGroup 需要遍历子 View 集合
for (int i = 0; i < count; i++) {
...
final View child = viewGroup.getChildAt(i);
if (childTag.endsWith("_0") && childTag.startsWith("layout") && childTag.indexOf('/') > 0) {
// include 标签处理
...
}
if (!isInclude) {
// ViewGroup 递归处理
mapBindings(bindingComponent, child, bindings, includes, viewsWithIds, false);
}
}
}
}

完成此步后,Object[] bindings 数组中即按序放置好了布局中的 View,ViewDataBinding 中直接可以使用:

1
2
3
4
this.mboundView0 = (ConstraintLayout) bindings[0];
this.mboundView0.setTag(null);
this.tv = (android.widget.TextView) bindings[1];
this.tv.setTag(null);

为什么说相对于 findViewById 效率更高?

因为 mapBindings 中对 View 寻找只会初次寻找并赋值一次,而 findViewById 会在 每次调用时 都从顶级 ViewGroup 递归遍历寻找 View

2.4 DirtyFlags 机制

DirtyFlags 是一个 64 位的 long 型数值,通过其每一个 bit 来标识一个成员变量数据是否发生了变化:

1
private  long mDirtyFlags = 0xffffffffffffffffL;

比如:

1
2
3
4
5
6
7
public void setVm(@Nullable ViewModel Vm) {
this.mVm = Vm;
synchronized(this) {
mDirtyFlags |= 0x4L;
}
...
}

调用 setVM 函数时,将 mDirtyFlags 和 0x4L 进行 或运算 ,0x4L 的二进制为 00 …. 0000 0100,进行或运算后,即将 mDirtyFlags 的第三位 bit 置为 1

1
2
3
4
5
6
7
8
if ((dirtyFlags & 0x4L) != 0) {
if (vm != null) {
vmText = vm.getText();
}
}
if ((dirtyFlags & 0x4L) != 0) {
TextViewBindingAdapter.setText(this.tv, vmText);
}

在更新变量的时候,则通过 mDirtyFlags 和 0x4L 进行 与运算 来判断该 bit 是否为 1,为 1 则说明需要刷新数据,再如判断是否有数据更新的 hasPendingBindings 方法:

1
2
3
4
5
6
7
8
9
@Override
public boolean hasPendingBindings() {
synchronized(this) {
if (mDirtyFlags != 0) {
return true;
}
}
return false;
}

既然通过 bit 来标识,而 mDirtyFlags 是一个 64 位的 long 型变量,岂不是只能设置 64 种数据?

其实不然,mDirtyFlags 确实无法容纳大于 64 种数据,但是超过 64 种数据 DataBinding 为为其再分配 mDirtyFlags_1、mDirtyFlags_2 等变量继续扩容,如:

1
2
3
private  long mDirtyFlags = 0xffffffffffffffffL;
private long mDirtyFlags_1 = 0xffffffffffffffffL;
private long mDirtyFlags_2 = 0xffffffffffffffffL;

小于 mDirtyFlags 范围使用 mDirtyFlags,超出 mDirtyFlags 范围使用 mDirtyFlags_1,以此类推:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void setA11(@Nullable ViewModel A11) {
updateRegistration(63, A11); // 从 0 开始,第 64 个数据
this.mA11 = A11;
synchronized(this) {
mDirtyFlags |= 0x8000000000000000L; // 使用 mDirtyFlags
}
...
}

public void setA35(@Nullable ViewModel A35) {
updateRegistration(64, A35); // 从 0 开始,第 65 个数据
this.mA35 = A35;
synchronized(this) {
mDirtyFlags_1 |= 0x1L; // 使用 mDirtyFlags_1
}
...
}

2.5 绑定并更新数据

当设置数据后,如何让数据展示到界面,一切从 ViewDataBinding#requestRebind 方法说起:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
protected void requestRebind() {
if (mContainingBinding != null) {
mContainingBinding.requestRebind(); // 直接调用父容器 requestRebind
} else {
synchronized (this) {
// 第一道风门
if (mPendingRebind) {
return;
}
mPendingRebind = true;
}
...
if (USE_CHOREOGRAPHER) { // boolean USE_CHOREOGRAPHER = SDK_INT >= 16;
mChoreographer.postFrameCallback(mFrameCallback);
} else {
mUIThreadHandler.post(mRebindRunnable);
}
}
}

mPendingRebind 起第一道风门作用,防止过度提交,需要等待 当前绑定数据的帧渲染完毕后 (或在 API < 16 时 主线程再次闲置 )才会再次 Rebind:

对于帧渲染回调,用的是 Choreographer,其在下一帧渲染信号时回调,回调后同样执行 mRebindRunnable 任务:

1
2
3
4
5
6
mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
mRebindRunnable.run();
}
};

mRebindRunnable 会在根布局 attach 到 window 的情况下执行 executePendingBindings 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private final Runnable mRebindRunnable = new Runnable() {
@Override
public void run() {
synchronized (this) {
mPendingRebind = false;
}
processReferenceQueue();

if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
if (!mRoot.isAttachedToWindow()) {
mRoot.removeOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
mRoot.addOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER); // 设置 attach 监听,重新执行 mRebindRunnable
return;
}
}
executePendingBindings();
}
};

executePendingBindings 方法会再次判断是否存在父容器的 ViewDataBinding,存在则执行父容器的 executePendingBindings 方法,否则,执行执行真正的绑定方法 executeBindingsInternal:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private void executeBindingsInternal() {
// 第二道风门
if (mIsExecutingPendingBindings) {
requestRebind();
return;
}
// 数据是否有变动
if (!hasPendingBindings()) {
return;
}
mIsExecutingPendingBindings = true;
mRebindHalted = false;
// 回调 OnRebindCallback
if (mRebindCallbacks != null) {
mRebindCallbacks.notifyCallbacks(this, REBIND, null); // 回调 onPreBind() 返回值确定是否拦截数据,置为 mRebindHalted 变量
if (mRebindHalted) { // 如果数据拦截
mRebindCallbacks.notifyCallbacks(this, HALTED, null);
}
}
if (!mRebindHalted) {
executeBindings();
if (mRebindCallbacks != null) {
mRebindCallbacks.notifyCallbacks(this, REBOUND, null);
}
}
mIsExecutingPendingBindings = false;
}

第二道风门由 mIsExecutingPendingBindings 变量控制,存在重复提交时,会将其重新导向 requestRebind,等待下一帧渲染时再执行。

最后由 executeBindings 实现方法完成数据的绑定:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
// 取出数据
ViewModel vm = mVm;
String vmText = null;
if ((dirtyFlags & 0x7L) != 0) {
if (vm != null) {
vmText = vm.getText();
}
}
// 对控件进行赋值
if ((dirtyFlags & 0x7L) != 0) {
TextViewBindingAdapter.setText(this.tv, vmText);
}
}

3 VM 触发 View 过程

1
2
3
4
public void setVm(@Nullable ViewModel Vm) {
updateRegistration(0, Vm);
...
}

在调用 ViewDataBinding 的 set 变量方法时都会执行 updateRegistration 方法,跟进到 registerTo 方法:

1
2
3
4
5
6
7
8
9
protected void registerTo(int localFieldId, Object observable, CreateWeakListener listenerCreator) {
...
listener = listenerCreator.create(this, localFieldId); // 创建 WeakPropertyListener 实例
mLocalFieldObservers[localFieldId] = listener;
if (mLifecycleOwner != null) {
listener.setLifecycleOwner(mLifecycleOwner);
}
listener.setTarget(observable);
}

其中 listenerCreator.create 方法的实现为:

1
2
3
public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();
}

来看 setTarget 方法,observable 参数即为我们的 ViewModel,即继承 BaseObservable 的类,跟进看它做了什么:

1
2
3
4
5
6
7
public void setTarget(T object) {
unregister();
mTarget = object;
if (mTarget != null) {
mObservable.addListener(mTarget); // mObservable 不是我们的 ViewModel,而是实现了 ObservableReference 接口的 WeakPropertyListener
}
}

来看 addListener 方法:

1
2
3
4
@Override
public void addListener(Observable target) {
target.addOnPropertyChangedCallback(this);
}

也就是说绕了一圈,为 Observable 类,即我们传入的 ViewModel,设置了属性回调监听,在 ViewModel 的成员属性发生变化时,都会回调 OnPropertyChangedCallback,继续跟进数据变化后会做什么事情:

1
2
3
4
5
6
@Override
public void onPropertyChanged(Observable sender, int propertyId) {
ViewDataBinding binder = mListener.getBinder();
...
binder.handleFieldChange(mListener.mLocalFieldId, sender, propertyId); // 触发 View 更新
}

WeakListener 的巧妙之处在于,其既持有了 ViewDataBinding,也持有 ViewModel 数据,这样在 ViewModel 变化时,通知 ViewDataBinding,ViewDataBinding 再触发 View 更新:

image-20181117183007111

继续跟进则来到 ViewDataBinding 的 onFieldChange 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Override
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
switch (localFieldId) {
case 0 :
return onChangeVm((top.wuhaojie.databindingdemo.ViewModel) object, fieldId);
}
return false;
}

private boolean onChangeVm(top.wuhaojie.databindingdemo.ViewModel Vm, int fieldId) {
if (fieldId == BR._all) {
synchronized(this) {
mDirtyFlags |= 0x1L;
}
return true;
}
else if (fieldId == BR.text) {
synchronized(this) {
mDirtyFlags |= 0x2L;
}
return true;
}
return false;
}

判断具体数据的变化,将 mDirtyFlags 置为对应的值,然后依照返回值执行 requestRebind 重新绑定以刷新 View:

1
2
3
4
5
6
7
private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
...
boolean result = onFieldChange(mLocalFieldId, object, fieldId);
if (result) {
requestRebind(); // 绑定以刷新 View
}
}

4 View 触发 VM 过程

View 变化触发 VM 更新的过程比较简单,就是 DataBinding 帮助开发者自动生成了为 View 控件设置变化监听的代码,以 EditText 控件为例:

1
2
3
4
5
6
7
8
9
10
@Override
protected void executeBindings() {
...
if ((dirtyFlags & 0x7L) != 0) {
TextViewBindingAdapter.setText(this.et, vmText);
}
if ((dirtyFlags & 0x4L) != 0) {
TextViewBindingAdapter.setTextWatcher(this.et, null, null, null, etandroidTextAttrChanged);
}
}

在控件回调方法中,对 ViewModel 进行更新值的操作:

1
2
3
4
5
6
@Override
public void onChange() {
String callbackArg_0 = TextViewBindingAdapter.getTextString(et);
...
vm.setText(((java.lang.String) (callbackArg_0)));
}

5 回调管理

DataBinding 中的回调管理统一采用 CallbackRegistry,其具备 回调时可重入修改延迟自动回收 两大特性。

5.1 标记逻辑

采用一个 long 型变量和一个 long[] 数组变量来对回调器的状态进行标记:

1
2
private long mFirst64Removed = 0x0;
private long[] mRemainderRemoved;

mFirst64Removed 变量用于对数目小于 64 的回调器进行快速标记:

image-20181117183053883

其中每一个 bit 对应一个回调器的状态,0 表示未被移除,1 表示已被移除。而对于数目大于 64 的情况,采用 mRemainderRemoved 这种 long 型数组的形式:

image-20181117183113778

由此可以计算第 n 个 bit 元素的位置,行数为 n / 64 - 1,列数为 n % 64,故该 bit 的值为 a [ n / 64 - 1 ] & ( 1L << ( n % 64 ) ),明白此一点对后续理解代码有帮助。

明白标记逻辑后,再来看代码中对 isRemoved 方法的实现就很好理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private boolean isRemoved(int index) {
if (index < Long.SIZE) {
// 小于 64 的情况下,使用 mFirst64Removed 变量判断
final long bitMask = 1L << index;
return (mFirst64Removed & bitMask) != 0;
} else if (mRemainderRemoved == null) {
return false;
} else {
// 行数
final int maskIndex = (index / Long.SIZE) - 1;
if (maskIndex >= mRemainderRemoved.length) {
return false;
} else {
final long bits = mRemainderRemoved[maskIndex];
// 列数
final long bitMask = 1L << (index % Long.SIZE);
return (bits & bitMask) != 0;
}
}
}

再来看设置移除标记位的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private void setRemovalBit(int index) {
if (index < Long.SIZE) {
// 小于 64 的情况,直接对 mFirst64Removed 进行或运算
final long bitMask = 1L << index;
mFirst64Removed |= bitMask;
} else {
// 确定行数
final int remainderIndex = (index / Long.SIZE) - 1;
if (mRemainderRemoved == null) {
// 懒式初始化
mRemainderRemoved = new long[mCallbacks.size() / Long.SIZE];
} else if (mRemainderRemoved.length < remainderIndex) {
// mRemainderRemoved 数组扩容
long[] newRemainders = new long[mCallbacks.size() / Long.SIZE];
System.arraycopy(mRemainderRemoved, 0, newRemainders, 0, mRemainderRemoved.length);
mRemainderRemoved = newRemainders;
}
// 确定 bit 所在列
final long bitMask = 1L << (index % Long.SIZE);
// 或运算
mRemainderRemoved[remainderIndex] |= bitMask;
}
}

mRemainderRemoved 中最后一个 bit 的序号不一定等于 mCallbacks.size,因为 mRemainderRemoved 只会在设置标记移除的时候才会扩容到当前的 mCallbacks.size

5.2 添加回调逻辑

1
private List<C> mCallbacks = new ArrayList<C>();

回调器都被添加到 List 中,List 的下标同时为移除标记中的 bit 位置:

1
2
3
4
5
6
7
8
9
public synchronized void add(C callback) {
if (callback == null) {
throw new IllegalArgumentException("callback cannot be null");
}
int index = mCallbacks.lastIndexOf(callback);
if (index < 0 || isRemoved(index)) {
mCallbacks.add(callback);
}
}

5.3 移除逻辑

移除回调器分为两种情况,一种是在闲置状态,可以直接移除回调,另一种是正在进行回调通知状态,此时只能标记移除,即「 假移除 」:

1
2
3
4
5
6
7
8
9
10
11
12
public synchronized void remove(C callback) {
if (mNotificationLevel == 0) {
// 闲置状态,直接移除
mCallbacks.remove(callback);
} else {
// 回调通知进行中,进行标记
int index = mCallbacks.lastIndexOf(callback);
if (index >= 0) {
setRemovalBit(index);
}
}
}

mNotificationLevel 控制可重入的回调通知层级,为 0 时表示回调通知执行完毕。而在每一次回调完成后,才会真正清理 回调通知进行过程中 被「 假移除 」的回调器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public synchronized void notifyCallbacks(T sender, int arg, A arg2) {
...
if (mNotificationLevel == 0) {
if (mRemainderRemoved != null) {
// 倒序遍历清除
for (int i = mRemainderRemoved.length - 1; i >= 0; i--) {
final long removedBits = mRemainderRemoved[i];
if (removedBits != 0) {
removeRemovedCallbacks((i + 1) * Long.SIZE, removedBits);
mRemainderRemoved[i] = 0;
}
}
}
// 处理小于 64 的情况
if (mFirst64Removed != 0) {
removeRemovedCallbacks(0, mFirst64Removed);
mFirst64Removed = 0;
}
}
}

removeRemovedCallbacks 的参数 (i + 1) * Long.SIZE 中 i + 1 的原因是:mRemainderRemoved 的范围是 >= 64,小于 64 的交给 mFirst64Removed 处理,避免 i = 0 的仍进行处理

1
2
3
4
5
6
7
8
9
10
11
12
13
private void removeRemovedCallbacks(int startIndex, long removed) {
// 清理一行的 64 个 bit
final int endIndex = startIndex + Long.SIZE;
long bitMask = 1L << (Long.SIZE - 1);
// 行数倒序遍历
for (int i = endIndex - 1; i >= startIndex; i--) {
if ((removed & bitMask) != 0) {
mCallbacks.remove(i);
}
// 每一行的 bit 从左到右遍历
bitMask >>>= 1;
}
}

5.4 递归回调

1
2
3
4
5
6
public synchronized void notifyCallbacks(T sender, int arg, A arg2) {
mNotificationLevel++; // 层级增加
notifyRecurse(sender, arg, arg2); // 递归回调通知
mNotificationLevel--; // 恢复
...
}

notifyCallbacks 方法是可重入调用的,mNotificationLevel 则用来控制当前重入的次数,继续跟进 notifyRecurse 方法:

1
2
3
4
5
6
7
8
9
10
11
12
private void notifyRecurse(T sender, int arg, A arg2) {
final int callbackCount = mCallbacks.size();
final int remainderIndex = mRemainderRemoved == null ? -1 : mRemainderRemoved.length - 1;

// 对具有 remove bit 的部分递归回调
notifyRemainder(sender, arg, arg2, remainderIndex);

// 剩余部分全部回调
// remainderIndex + 2 是因为 notifyRemainder 中最大范围处理到了 remainderIndex + 1
final int startCallbackIndex = (remainderIndex + 2) * Long.SIZE;
notifyCallbacks(sender, arg, arg2, startCallbackIndex, callbackCount, 0);
}

该方法是将 mCallbacks 分成两个部分进行通知处理:

image-20181117183233661

第一部分,标记有 RemoveBit 的部分,即最大范围为标记 bit 时 mCallbacks 的大小;

第二部分,剩余部分,该部分从未标记过 bit,可以全部进行通知;

1
2
3
4
5
6
7
8
9
10
11
12
13
private void notifyRemainder(T sender, int arg, A arg2, int remainderIndex) {
if (remainderIndex < 0) {
// 递归结束,此时处理到 mRemainderRemoved[0],应该转交给 mFirst64Removed
notifyFirst64(sender, arg, arg2);
} else {
final long bits = mRemainderRemoved[remainderIndex];
final int startIndex = (remainderIndex + 1) * Long.SIZE; // remainderIndex + 1 同样是为了避免 remainderIndex[0]
final int endIndex = Math.min(mCallbacks.size(), startIndex + Long.SIZE);
notifyRemainder(sender, arg, arg2, remainderIndex - 1); // 递归
// 从 startIndex 到 endIndex 按照 mRemainderRemoved[remainderIndex] 标记来进行回调通知
notifyCallbacks(sender, arg, arg2, startIndex, endIndex, bits);
}
}

最后按照每一行右边低位到左边高位的顺序进行通知:

1
2
3
4
5
6
7
8
9
private void notifyCallbacks(T sender, int arg, A arg2, final int startIndex, final int endIndex, final long bits) {
long bitMask = 1;
for (int i = startIndex; i < endIndex; i++) {
if ((bits & bitMask) == 0) {
mNotifier.onNotifyCallback(mCallbacks.get(i), sender, arg, arg2);
}
bitMask <<= 1;
}
}

6 BindAdapter 工作流程

@ BindAdapter 注解可以让开发者自定义布局属性值完成自定义的 setter 方法,看起来像 Hook 了方法,其实很简单,答案在生成的 ViewDataBinding 的执行绑定方法中:

1
2
3
4
5
6
7
@Override
protected void executeBindings() {
...
if ((dirtyFlags & 0x4L) != 0) {
Adapters.setSize(this.tv, 100); // 直接调用自定义的 BindingAdapters
}
}

7 和 LiveData 协作

跟进 executeBindings 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
protected void executeBindings() {
...
if ((dirtyFlags & 0x7L) != 0) {

if (vm != null) {
vmText = vm.text;
}
updateLiveDataRegistration(0, vmText);

if (vmText != null) {
vmTextGetValue = vmText.getValue();
}
}
}

原理都一致,在「 桥梁 」中为 ViewModel 设置监听,然后通知 ViewDataBinding,再刷新 View,来看「 桥梁 」:

1
2
3
4
5
6
@Override
public void addListener(LiveData<?> target) {
if (mLifecycleOwner != null) {
target.observe(mLifecycleOwner, this); // 设置监听
}
}

数据变化后回调:

1
2
3
4
5
@Override
public void onChanged(@Nullable Object o) {
ViewDataBinding binder = mListener.getBinder();
binder.handleFieldChange(mListener.mLocalFieldId, mListener.getTarget(), 0);
}

8 最后几句

至此,应该能回答以下问题:

1、View 的初始化也是像 ButterKnife 一样用 APT 批量帮我们写了 findViewById 方法吗?

2、BaseObservable#addOnPropertyChangedCallback 可以重复添加吗?remove 时如果正在执行过程会不会影响到界面渲染等其它操作?

3、ViewModel 的数据更新快速变化,如 1s 内数据从 1 递增到 100000,OnPropertyChangedCallback 会被回调 100000 次吗?界面会被刷新 100000 次吗?如果不是,数据如何背压丢弃?

4、@ BindingAdapter 自定义属性对系统方法进行 Hook 了吗?会不会影响性能?

5、View 变化是如何让 ViewModel 对应变化的?