Android DataBinding单向数据绑定深入探究

2022-11-16 432阅读

温馨提示:这篇文章已超过519天没有更新,请注意相关的内容是否还可用!

在前面DataBinding原理----布局的加载这篇文章中,我们说明了DataBinding中布局的加载过程,这里继续下一步,数据是如何进行绑定的,这里只介绍单向数据绑定,即数据的变化会反映到控件上;后面再介绍双向数据绑定。

在分析源码之前,在心里要有一个概念就是这里的数据绑定是基于观察者模式来实现的,所以在阅读这部分源码的时候要着重分清楚,谁是观察者谁是被观察者,把这个思想放在心理,这样就能抓住代码的本质。

这一篇分为两个小部分,首先是数据的绑定流程分析,然后是观察者模式绑定关系的建立流程。

一、数据绑定流程

代码分析,走起。还是贴一段Activity的代码如下:

class MainActivity : AppCompatActivity() {
private val viewModel: SimpleViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.lifecycleOwner = this
binding.viewModel = viewModel
}
}

数据绑定的关键就是binding.viewModel = viewModel这行代码,其调用的方法实现如下:

public void setViewModel(@Nullable com.zfang.databindingstudy.module.SimpleViewModel ViewModel) {
this.mViewModel = ViewModel;
synchronized(this) {
mDirtyFlags |= 0x4L;
}
notifyPropertyChanged(BR.viewModel);
super.requestRebind();
}

notifyPropertyChanged这一行其实就是在通知观察者,数据发生变化了。不过这不是我们这里关注的重点(不过注意这里的mDirtyFlags 已经被修改了,已经有值)。重点在后面的那一行super.requestRebind(),其实现如下:

  protected void requestRebind() {
//处理有include标签的情况
if (mContainingBinding != null) {
mContainingBinding.requestRebind();
} else {
//我们的场景下走的是这里
final LifecycleOwner owner = this.mLifecycleOwner;
if (owner != null) {
//如果生命周期不是至少STARTED则返回
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;
}
if (USE_CHOREOGRAPHER) {
//走这里,使用垂直同步刷新
mChoreographer.postFrameCallback(mFrameCallback);
} else {
mUIThreadHandler.post(mRebindRunnable);
}
}
}

首先判断是不是属于include标签的情况(也就是布局中包含include标签),如果不是则走到else分支,然后判断生命周期是不是至少STARTED状态,如果不是则不执行数据绑定(生命周期恢复的时候会再执行数据绑定);如果满足生命周期要求,则继续判断,首先把mPendingRebind 置位(避免重复绑定数据),然后使用垂直同步刷新机制post了一个callback,callback实现(位于ViewDataBinding的构建函数中)如下:

if (USE_CHOREOGRAPHER) {
mChoreographer = Choreographer.getInstance();
mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
mRebindRunnable.run();
}
};
} else {
mFrameCallback = null;
mUIThreadHandler = new Handler(Looper.myLooper());
}

其实最终走的还是mRebindRunnable,其实现如下:

private final Runnable mRebindRunnable = new Runnable() {
@Override
public void run() {
synchronized (this) {
mPendingRebind = false;
}
//处理弱引用队列中的监听器,避免内在泄漏
processReferenceQueue();
//如果是Android4.4及以后走这里
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
// Nested so that we don't get a lint warning in IntelliJ
//如果View还没attach上,则返回;后面attach上会再执行数据绑定
if (!mRoot.isAttachedToWindow()) {
// Don't execute the pending bindings until the View
// is attached again.
mRoot.removeOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
mRoot.addOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
return;
}
}
//执行数据绑定.
executePendingBindings();
}
};

嗯,代码中都写了注释。首先处理弱引用队列,避免内存泄漏,然后判断如果View还没attach则返回,后面attach上再执行数据绑定。最后调用executePendingBindings,执行数据绑定。

public void executePendingBindings() {
if (mContainingBinding == null) {
//没有include会走这里
executeBindingsInternal();
} else {
mContainingBinding.executePendingBindings();
}
}

我们的场景下走executeBindingsInternal,代码如下:

private void executeBindingsInternal() {
if (mIsExecutingPendingBindings) {
requestRebind();
return;
}
//这里调用了apt代码生存的方法,也就是我们工程中的方法。
if (!hasPendingBindings()) {
return;
}
mIsExecutingPendingBindings = true;
mRebindHalted = false;
if (mRebindCallbacks != null) {
mRebindCallbacks.notifyCallbacks(this, REBIND, null);
// The onRebindListeners will change mPendingHalted
if (mRebindHalted) {
mRebindCallbacks.notifyCallbacks(this, HALTED, null);
}
}
if (!mRebindHalted) {
//这里执行绑定
executeBindings();
if (mRebindCallbacks != null) {
mRebindCallbacks.notifyCallbacks(this, REBOUND, null);
}
}
mIsExecutingPendingBindings = false;
}

上面主要是一些条件判断,避免重复执行绑定。其中方法hasPendingBindings判断有没有需要执行数据绑定,最后executeBindings调用执行数据绑定;hasPendingBindings的实现位于ActivityMainBindingImpl.java中,也就是DataBinding帮我们生存的类,代码如下:

   //ActivityMainBindingImpl.java
   public boolean hasPendingBindings() {
synchronized(this) {
if (mDirtyFlags != 0) {
return true;
}
}
return false;
}

上面我们说过mDirtyFlags 在setViewModel方法调用的时候参数已经不为0了,所以这里会返回true,也就是后面会执行到executeBindings方法,其实现也位于ActivityMainBindingImpl.java类中,代码如下:

@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized (this) {
//复制位标识,记录了哪些数据发生变化。
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
java.lang.String viewModelFirstGetValue = null;
androidx.lifecycle.LiveData<java.lang.String> viewModelSecond = null;
androidx.lifecycle.LiveData<java.lang.String> viewModelFirst = null;
java.lang.String viewModelSecondGetValue = null;
com.zfang.databindingstudy.module.SimpleViewModel viewModel = mViewModel;
//根据dirtyFlags 判断哪些数据发生变化,一个变量会对应到二进制里面的一个位
//该位不为0,说明数据有变化,于是会执行相应的获得数据逻辑。
if ((dirtyFlags & 0xfL) != 0) {
if ((dirtyFlags & 0xdL) != 0) {
if (viewModel != null) {
// read viewModel.second
viewModelSecond = viewModel.getSecond();
}
updateLiveDataRegistration(0, viewModelSecond);
if (viewModelSecond != null) {
// read viewModel.second.getValue()
viewModelSecondGetValue = viewModelSecond.getValue();
}
}
if ((dirtyFlags & 0xeL) != 0) {
if (viewModel != null) {
// read viewModel.first
viewModelFirst = viewModel.getFirst();
}
//建立观察者绑定关系
updateLiveDataRegistration(1, viewModelFirst);
if (viewModelFirst != null) {
// read viewModel.first.getValue()
viewModelFirstGetValue = viewModelFirst.getValue();
}
}
}
// batch finished
//根据上面拿到的数据调用setText,也就是设置了相应的数据到UI上,实现
//了数据的变化更新的UI逻辑。
if ((dirtyFlags & 0xeL) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.first, viewModelFirstGetValue);
}
if ((dirtyFlags & 0xdL) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.second, viewModelSecondGetValue);
}
}
// Listener Stub Implementations
// callback impls
// dirty flag
private long mDirtyFlags = 0xffffffffffffffffL;
/* flag mapping
flag 0 (0x1L): viewModel.second
flag 1 (0x2L): viewModel.first
flag 2 (0x3L): viewModel
flag 3 (0x4L): null
flag mapping end*/
//end

首先说明下,我们的model里面一般来说会有几个变量,其实每一个变量在DataBinding中都会有一个二进制位来标识当前数据是否发生了变化,如果发生变化,则该位置1,然后用dirtyFlags 作一个“与”运算就可以判断出该数据位是否发生了变化(好好体会下)。

对应到我们的场景中,flag 0 (0x1L): viewModel.second这个标识就对应到second这个变量,而flag 1 (0x2L): viewModel.first就对应到first这个变量,再用dirtyFlags作运算就知道哪个位发生变化。

等等,数据是已经绑定了,怎么没看到观察者模式的绑定关系?其实上面的代码注释已经说明了,这行代码updateLiveDataRegistration正是建立了观察者模式的绑定关系。

终于看到数据的绑定了,下面继续看下updateLiveDataRegistration是如何建立观察者模式的绑定关系。

二、建立观察者模式绑定关系

首先贴上updateLiveDataRegistration的代码如下:

//ViewDataBinding.java
protected boolean updateLiveDataRegistration(int localFieldId, LiveData<?> observable) {
mInLiveDataRegisterObserver = true;
try {
return updateRegistration(localFieldId, observable, CREATE_LIVE_DATA_LISTENER);
} finally {
mInLiveDataRegisterObserver = false;
}
}

首先作下参数说明,第一个参数说明是哪个位置上的变量,第二个是个LiveData(对应到了我们在ViewModel中定义的数据变量,也就是要被观察的变量)。这里只是简单的转发调用下updateRegistration,最后一个参数是一个creator(利用了工厂方法模式),其实现如下:

private static final CreateWeakListener CREATE_LIVE_DATA_LISTENER = new CreateWeakListener() {
@Override
public WeakListener create(
ViewDataBinding viewDataBinding,
int localFieldId,
ReferenceQueue<ViewDataBinding> referenceQueue
) {
return new LiveDataListener(viewDataBinding, localFieldId, referenceQueue)
.getListener();
}
};

就是创建了一个LiveDataListener对象,并调用了它的getListener方法,该方法会返回一个WeakListener类型的变量。继续上面的updateRegistration方法,代码如下:

protected boolean updateRegistration(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
//由于observable是一个livedata对象且不空,所以不会走这里。
if (observable == null) {
return unregisterFrom(localFieldId);
}
WeakListener listener = mLocalFieldObservers[localFieldId];
//因为是第一次进入这里,所以会进入到registerTo中
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) {
//上面说过这里返回的是个WeakListener对象。
listener = listenerCreator.create(this, localFieldId, sReferenceQueue);
//把对象保存起来
mLocalFieldObservers[localFieldId] = listener;
//设置lifeCycle
if (mLifecycleOwner != null) {
listener.setLifecycleOwner(mLifecycleOwner);
}
}
//建立观察者绑定关系
listener.setTarget(observable);
}

首先判断如果listener则创建,返回的是一个WeakListener对象(其实中间还有一个LiveDataListener对象,WeakListener对象的mObservable变量会持有LiveDataListener对象),然后保存到mLocalFieldObservers数组中,看上去就是一个观察者数组对象,最后调用WeakListener的setTarget方法,其实现如下:

public void setTarget(T object) {
//这里的object就是传递过来的liveData对象,下面会把它保存到target中
unregister();
mTarget = object;
if (mTarget != null) {
//mObservable其实就是LiveDataListener对象
mObservable.addListener(mTarget);
}
}

重复下,上面的mTarget就是ViewModel中的LiveData对象,mObservable其实就是LiveDataListener对象,所以调用了LiveDataListener的addListener方法,实现如下:

//ViewDataBinding$LiveDataListener
public void addListener(LiveData<?> target) {
//target是LiveData
LifecycleOwner lifecycleOwner = getLifecycleOwner();
if (lifecycleOwner != null) {
//LiveData监听了lifecycleOwner的生命周期变化。
target.observe(lifecycleOwner, this);
}
}

很简单的逻辑,就是通过target.observe(lifecycleOwner, this)建立了观察者模式的绑定关系。之后生命周期发生变化就会调用到onChange方法,代码如下:

public void onChanged(@Nullable Object o) {
ViewDataBinding binder = mListener.getBinder();
if (binder != null) {
binder.handleFieldChange(mListener.mLocalFieldId, mListener.getTarget(), 0);
}
}

这里的binder其实就是ActivityMainBindingImpl,随后调用handleFieldChange,代码如下:

protected void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
if (mInLiveDataRegisterObserver || mInStateFlowRegisterObserver) {
// We're in LiveData or StateFlow registration, which always results in a field change
// that we can ignore. The value will be read immediately after anyway, so
// there is no need to be dirty.
return;
}
//调用了子类实现,也就是ActivityMainBindingImpl
boolean result = onFieldChange(mLocalFieldId, object, fieldId);
if (result) {
//重新绑定数据
requestRebind();
}
}

首先调用了onFiledChange,它的实现位于apt生存的代码中,如下:

@Override
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
switch (localFieldId) {
case 0 :
return onChangeViewModelSecond((androidx.lifecycle.LiveData<java.lang.String>) object, fieldId);
case 1 :
return onChangeViewModelFirst((androidx.lifecycle.LiveData<java.lang.String>) object, fieldId);
}
return false;
}
private boolean onChangeViewModelSecond(androidx.lifecycle.LiveData<java.lang.String> ViewModelSecond, int fieldId) {
if (fieldId == BR._all) {
synchronized(this) {
mDirtyFlags |= 0x1L;
}
return true;
}
return false;
}
private boolean onChangeViewModelFirst(androidx.lifecycle.LiveData<java.lang.String> ViewModelFirst, int fieldId) {
if (fieldId == BR._all) {
synchronized(this) {
mDirtyFlags |= 0x2L;
}
return true;
}
return false;
}

根据哪些数据发生变化,返回true或者false,如果返回true,之后会调用requestRebind,不正是对应开了开头中分析的函数了吗?形成闭环。从而完成了整个观察者模式的建立与响应流程。

这一篇中分析了数据的绑定与观察者模式的建立流程,如果想了解DataBinding中布局的加载可以看前一篇DataBinding原理----布局的加载。下一篇将会分析双向数据绑定流程。