Jetpack:DataBinding
创始人
2025-06-01 07:25:53
0

文章目录

  • DataBinding 的使用
  • DataBinding 的布局绑定过程
  • DataBinding 是如何做到数据驱动 UI 的?
    • 注册监听流程
    • 刷新 UI 流程
    • 通知更新 UI 流程
  • 总结

DataBinding 的使用

关于 DataBinding 如何使用 官方文档 已经有详细介绍,该篇文章主要讲解的 DataBinding 的原理,所以基本使用不会赘述。

个人使用 DataBinding 认为还是有使用的注意事项,DataBinding 框架虽然非常强大能处理很多数据绑定操作,但不建议将过多的操作都放在 xml 做数据绑定,应保持尽量以刷新 UI 更新的角度使用 DataBinding,否则当出现问题时,可能会存在难以定位的问题。

DataBinding 的布局绑定过程

在使用 DataBinding 时,为了能做到控件与数据的绑定,xml 会有如下更改:

activity_main.xml


xml 的根节点是 标签, 标签提供数据相关的信息和类,数据绑定通过 @{} 描述说明。xml 文件描述都更改了,inflater dom 解析并不会识别新增的这些标签,那么 DataBinding 是怎么识别的?

/app/build/imtermediates/incremental/mergeDebugResources/stripped.dir/layout/activity_main.xml
 
/app/build/imtermediates/data_binding_layout_info_type_merge/debug/out/activity_main-layout.xml
false

当我们将项目 build 重新构建时,DataBinding 会将我们的布局 xml 文件拆成了两个 xml 文件:

  • /app/build/imtermediates/incremental/mergeDebugResources/stripped.dir/layout/activity_main.xml

  • /app/build/imtermediates/data_binding_layout_info_type_merge/debug/out/activity_main-layout.xml

为了讲解方便,我将前者称为控件数据标记文件,后者称为控件数据查找文件。

DataBinding 将布局中使用了 @{} 的 View 生成 tag 属性标记,去控件数据查找文件就能查找到绑定信息。综合两个 xml 文件就能做到将控件和绑定信息的关系描述清楚。

如上例 TextView 生成了 tag 属性标记为 binding_1,当数据更新时就能通过 binding_1 找到属性 android:text 绑定的数据是 user.name。

或许你会有疑问:标记?查找绑定?那具体又是怎么个标记和查找绑定?

我们继续分析。先写一个简单使用 DataBinding 的 demo:

public class User extends BaseObservable {private String name;private String pwd;public User(String name, String pwd) {this.name = name;this.pwd = pwd;}@Bindablepublic String getName() {return name;}@Bindablepublic String getPwd() {return pwd;}public void setName(String name) {this.name = name;notifyPropertyChanged(BR.name);}public void setPwd(String pwd) {this.pwd = pwd;notifyPropertyChanged(BR.pwd);}
}class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)val binding =DataBindingUtil.setContentView(this, R.layout.activity_main)binding.user = User("vincent", "12345")// binding.textView}
}

上面的 demo 非常简单,创建一个 User 对象,将 User 对象的属性和 xml 中的控件绑定。Activity 对界面的绑定也不是用的 setContentView(),而是通过 DataBindingUtils.setContentView()。

使用 DataBinding 的时候我们可以很方便的通过 binding.textView 的方式获取到控件,它是怎么做到的?DataBindingUtils.setContentView() 到底做了什么事情?

DataBindingUtils.javapublic static  T setContentView(@NonNull Activity activity,int layoutId) {return setContentView(activity, layoutId, sDefaultComponent);
}public static  T setContentView(@NonNull Activity activity,int layoutId, @Nullable DataBindingComponent bindingComponent) {// 还是调用 Activity 的 setContentView() 填充布局activity.setContentView(layoutId);View decorView = activity.getWindow().getDecorView();ViewGroup contentView = (ViewGroup) decorView.findViewById(android.R.id.content);return bindToAddedViews(bindingComponent, contentView, 0, layoutId);
}private static  T bindToAddedViews(DataBindingComponent component,ViewGroup parent, int startChildren, int layoutId) {final int endChildren = parent.getChildCount();final int childrenAdded = endChildren - startChildren;if (childrenAdded == 1) {// 如果我们的布局只有一个控件,调用 bind() 绑定final View childView = parent.getChildAt(endChildren - 1);return bind(component, childView, layoutId);} else {// 如果布局有多个控件,循环获取布局的控件,然后 bind() 绑定final View[] children = new View[childrenAdded];for (int i = 0; i < childrenAdded; i++) {children[i] = parent.getChildAt(i + startChildren);}return bind(component, children, layoutId);}
}// DataBinderMapperImpl 是 APT 生成的类
private static DataBinderMapper sMapper = new DataBinderMapperImpl();static  T bind(DataBindingComponent bindingComponent, View[] roots,int layoutId) {return (T) sMapper.getDataBinder(bindingComponent, roots, layoutId);
}

DataBindingUtils.setContentView() 通过源码分析,其实还是调用的 Activity 的 setContentView(),然后拿到 DecorView 下的 FrameLayout(android.R.id.content),将我们布局中的每个控件进行绑定。

其中 sMapper 是 APT 生成的类,文件生成在 /app/build/generated/ap_generated_sources/debug/out/com/example/demo/databinding/DataBindingMapperImpl.java

DataBindingMapperImpl.javaprivate static final int LAYOUT_ACTIVITYMAIN = 1;private static final SparseIntArray INTERNAL_LAYOUT_ID_LOOKUP = new SparseIntArray(1);static {INTERNAL_LAYOUT_ID_LOOKUP.put(com.example.demo.R.layout.activity_main, LAYOUT_ACTIVITYMAIN);
}@Override
public ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) {int localizedLayoutId = INTERNAL_LAYOUT_ID_LOOKUP.get(layoutId);if(localizedLayoutId > 0) {final Object tag = view.getTag();if(tag == null) {throw new RuntimeException("view must have a tag");}switch(localizedLayoutId) {case  LAYOUT_ACTIVITYMAIN: {// /app/build/imtermediates/incremental/mergeDebugResources/stripped.dir/layout/activity_main.xml// xml 文件的根节点 tagif ("layout/activity_main_0".equals(tag)) {// APT 生成的实现类return new ActivityMainBindingImpl(component, view);}throw new IllegalArgumentException("The tag for activity_main is invalid. Received: " + tag);}}}return null;
}

getDataBinder() 就做了一件事情,根据根布局生成的 tag 标记 layout/activity_main_0 创建对应界面的 ViewDataBinding 即 ActivityMainBindingImpl,在它的构造函数中就做了控件绑定的操作,让我们能够通过 binding.textView 的方式拿到控件:

ActivityMainBindingImpl.javapublic ActivityMainBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {// 数字 2 表示布局绑定的控件数量this(bindingComponent, root, mapBindings(bindingComponent, root, 2, sIncludes, sViewsWithIds));
}private ActivityMainBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {super(bindingComponent, root, 1, (android.widget.TextView) bindings[1] // 控件绑定);this.mboundView0 = (android.widget.LinearLayout) bindings[0];this.mboundView0.setTag(null);this.textView.setTag(null);setRootTag(root);// listenersinvalidateAll();
}ViewDataBinding.javaprotected static Object[] mapBindings(DataBindingComponent bindingComponent, View root,int numBindings, IncludedLayouts includes, SparseIntArray viewsWithIds) {Object[] bindings = new Object[numBindings];// mapBindings() 把 View 对象都存到 bindings 数组里面// 应用层就能够直接 binding.textView 使用mapBindings(bindingComponent, root, bindings, includes, viewsWithIds, true);return bindings;
}

构造函数中最主要的一句代码是 mapBindings(),该方法将我们布局中的 View 都存到数组中,最终应用层就可以通过 binding.textView 的方式拿到布局中的 View。

我们梳理下 DataBindingUtils.setContentView() 做了什么事情:

  • 通过 Activity 的 setContentView() 加载布局

  • 根据布局根节点的 tag 属性 layout/activity_xxx_0 创建对应布局的 ViewDataBinding

  • 在 ViewDataBinding 创建时遍历布局所有的 View 存到数组中,数组的 View 赋值给 ViewDataBinding 的控件变量。实现可以通过 binding 获取控件 View

DataBinding 是如何做到数据驱动 UI 的?

DataBinding 使用的是观察者模式,只是这个观察者模式的变种比较大。既然是观察者模式,还是会需要有注册、监听和通知三个操作。那么 DataBinding 是在什么时候开始做注册的?

class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)val binding =DataBindingUtil.setContentView(this, R.layout.activity_main)binding.user = User("vincent", "12345")// binding.textView}
}

上面的代码是创建 ActivityMainBindingImpl 后,调用了 setUser() 将我们要提供数据的对象传给了 DataBinding。

ActivityMainBindingImpl.javapublic void setUser(@Nullable com.example.demo.User User) {updateRegistration(0, User);this.mUser = User;synchronized(this) {mDirtyFlags |= 0x1L;}notifyPropertyChanged(BR.user);super.requestRebind();
}

上面的代码虽然很简短,但是却做了几个操作:

  • updateRegistration():注册监听流程,绑定观察者 observer 和被观察者 observable

  • mDirtyFlags:数据是否更新标志

  • super.requestRebind():请求执行界面刷新

注册监听流程

因为涉及的类比较多,在分析时我们需要时刻记得区分好观察者和被观察者,否则很容易会出现分析了流程,但是都不知道这些类是用来干嘛的。

ViewDataBinding.java// 存储观察者的数组
private WeakListener[] mLocalFieldObservers;private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {@Overridepublic WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();}
};// localFieldId:每个数据对象都会分配一个数据对象绑定 id
// observable:数据对象 User,被观察者
// CREATE_PROPERTY_LISTENER:用于创建 WeakPropertyListener 和 WeakListener
// WeakListener:持有 DataBinding、数据对象绑定 id、数据对象
protected boolean updateRegistration(int localFieldId, Observable observable) {return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
}private boolean updateRegistration(int localFieldId, Object observable,CreateWeakListener listenerCreator) {...WeakListener listener = mLocalFieldObservers[localFieldId];if (listener == null) {// 第一次获取为 null,走此处的注册过程// listenerCreator 是 CREATE_PROPERTY_LISTENERregisterTo(localFieldId, observable, listenerCreator);return true;}...return true;
}protected void registerTo(int localFieldId, Object observable,CreateWeakListener listenerCreator) {if (observable == null) {return;}WeakListener listener = mLocalFieldObservers[localFieldId];if (listener == null) {// CREATE_PROPERTY_LISTENER.create() 创建 WeakPropertyListener// WeakPropertyListener 会创建 WeakListenerlistener = listenerCreator.create(this, localFieldId);mLocalFieldObservers[localFieldId] = listener;...}// 注册数据监听listener.setTarget(observable);
}

通过分析 updateRegistration() 我们列出几个比较重要信息和区分下观察者和被观察者:

  • localFieldId:数据对象绑定 id。用于查找存储 WeakListener 的数组索引

  • observable:数据对象 User,被观察者

  • WeakPropertyListener:持有 WeakListener

  • WeakListener:ViewDataBinding、数据对象绑定 id、数据对象的包装类,观察者。一个 WeakListener 对应一个数据对象

源码中 mLocalFieldObservers 虽然是存储 WeakListener 的观察者数组,通过源码分析它只是临时记录用于解除绑定操作,和数据绑定通知 UI 刷新无关,从注册流程角度分析我们可以忽略它的作用。

updateRegistration() 内调用 registerTo(),创建 WeakListener 并且存储到 mLocalFieldObservers 数组中。从这个角度分析,ViewDataBinding 是被观察者。

在这里插入图片描述

我们继续看 registerTo() 的 listener.setTarget(observable) 做了什么事情:

private static class WeakPropertyListener extends Observable.OnPropertyChangedCallbackimplements ObservableReference {final WeakListener mListener;public WeakPropertyListener(ViewDataBinding binder, int localFieldId) {// 创建 WeakPropertyListener 的同时创建了 WeakListenermListener = new WeakListener(binder, localFieldId, this);}@Overridepublic WeakListener getListener() {return mListener;}@Overridepublic void addListener(Observable target) {// 注册数据更新监听回调,target 是 User 对象target.addOnPropertyChangedCallback(this);}@Overridepublic void onPropertyChanged(Observable sender, int propertyId) {}...
}private static class WeakListener extends WeakReference {private final ObservableReference mObservable;protected final int mLocalFieldId; // 数据对象绑定 idprivate T mTarget; // 数据对象public WeakListener(ViewDataBinding binder, int localFieldId,ObservableReference observable) {super(binder, sReferenceQueue);mLocalFieldId = localFieldId;mObservable = observable; // WeakPropertyListener}public void setTarget(T object) {unregister();mTarget = object; // 记录数据对象 Userif (mTarget != null) {// 调用 WeakPropertyListener 的 addListenermObservable.addListener(mTarget);}}...
}public class BaseObservable implements Observable {private transient PropertyChangeRegistry mCallbacks;public BaseObservable() {}// OnPropertyChangedCallback 就是 WeakPropertyListener// WeakPropertyListener 持有 WeakListener// WeakListener 保存有 ViewDataBinding、数据对象绑定 id、数据对象@Overridepublic void addOnPropertyChangedCallback(@NonNull OnPropertyChangedCallback callback) {synchronized (this) {if (mCallbacks == null) {mCallbacks = new PropertyChangeRegistry();}}mCallbacks.add(callback);}
}public class PropertyChangeRegistry extendsCallbackRegistry {
}public class CallbackRegistry implements Cloneable {// 存储 WeakPropertyListener 的列表private List mCallbacks = new ArrayList();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);}}
}
  • PropertyChangeRegistry/CallbackRegistry:为了方便理解,可以将它理解为 WeakListener 观察者列表

listener.setTarget(observable) 其实就是注册观察者的操作,将 WeakPropertyListener 存储到 CallbackRegistry 的观察者列表。

在这里插入图片描述

简单总结下 updateRegistration() 就做了两件事情:

  • 为每一个需要绑定的数据对象提供一个数据对象绑定 id,创建 WeakListener,WeakListener 是 ViewDataBinding、数据对象绑定 id、数据对象 User 的包装类

  • 创建 CallbackRegistry,把它理解为 Observable 被观察者,将 WeakListener 注册到列表存储

为了方便理解,我们用伪代码描述下 updateRegistration():

public class ActivityMainBindingImpl {public void updateRegistration(User user) {registerTo(0, user);}	public void registerTo(int localFieldId, User user) {WeakListener listener = new WeakListener(this, localFieldId);listener.setTarget(user);}
}public class WeakListener {private ViewDataBinding binder;private int localFieldId;private User user;public WeakListener(ViewDataBinding binder, int localFieldId) {this.binder = binder;this.localFieldId = localFieldId;}public void setTarget(User user) {user.addOnPropertyChangedCallback(this);}
}public class User extends BaseObservable {// PropertyChangeRegistry/CallbackRegitry 本质上就是对这个观察者列表的操作private List mCallbacks;// 其实就是注册观察者的操作public void addOnPropertyChangedCallback(WeakListener callback) {mCallbacks.add(callback);}
}

具体流程如下:

在这里插入图片描述

刷新 UI 流程

在上面经过 updateRegistration() 完成了注册观察者的操作后,setUser() 的流程还没结束:

ActivityMainBindingImpl.javapublic void setUser(@Nullable com.example.demo.User User) {updateRegistration(0, User);this.mUser = User;synchronized(this) {mDirtyFlags |= 0x1L;}notifyPropertyChanged(BR.user);super.requestRebind();
}

紧接着修改了 mDirtyFlags,并且调用了 super.requestRebind():

protected void requestRebind() {...if (USE_CHOREOGRAPHER) {mChoreographer.postFrameCallback(mFrameCallback);} else {mUIThreadHandler.post(mRebindRunnable);}
}protected ViewDataBinding(DataBindingComponent bindingComponent, View root, int localFieldCount) {...if (USE_CHOREOGRAPHER) {// 通过编舞者每 16.6ms 接收一个 VSYNC 信号刷新 UImChoreographer = Choreographer.getInstance();mFrameCallback = new Choreographer.FrameCallback() {@Overridepublic void doFrame(long frameTimeNanos) {mRebindRunnable.run();}};} else {mFrameCallback = null;mUIThreadHandler = new Handler(Looper.myLooper());}
}private final Runnable mRebindRunnable = new Runnable() {@Overridepublic void run() {...executePendingBindings();}
};public void executePendingBindings() {if (mContainingBinding == null) {executeBindingsInternal();} else {mContainingBinding.executePendingBindings();}
}private void executeBindingsInternal() {...executeBindings();...
}ActivityMainBindingImpl.java@Override
protected void executeBindings() {long dirtyFlags = 0;synchronized(this) {dirtyFlags = mDirtyFlags;mDirtyFlags = 0;}java.lang.String userName = null;com.example.demo.User user = mUser;// 判断 dirtyFlags 是否有修改if ((dirtyFlags & 0x7L) != 0) {if (user != null) {// read user.nameuserName = user.getName();}}// batch finishedif ((dirtyFlags & 0x7L) != 0) {// 更新 UIandroidx.databinding.adapters.TextViewBindingAdapter.setText(this.textView, userName);}
}

DataBinding 刷新 UI 的方式是通过注册编舞者 Choreographer,每 16.6ms 接收一个 VSYNC 信号,再结合 mDirtyFlags 判断数值是否有修改,如果需要更新在下一个 VSYNC 信号到来时刷新 UI

通知更新 UI 流程

你可能有留意到在 setUser() 时我将 notifyPropertyChanged(BR.user) 忽略跳过了,其实是因为首次注册监听绑定时调用这句代码不会处理更新操作。但是我们数据更新通知刷新 UI 就是使用的该方法:

public class User extends BaseObservable {private String name;private String pwd;public User(String name, String pwd) {this.name = name;this.pwd = pwd;}@Bindablepublic String getName() {return name;}@Bindablepublic String getPwd() {return pwd;}public void setName(String name) {this.name = name;// 通知更新 UInotifyPropertyChanged(BR.name);}public void setPwd(String pwd) {this.pwd = pwd;// 通知更新 UInotifyPropertyChanged(BR.pwd);}
}

我们以 setName() 为例子讲解下 notifyPropertyChanged(id) 的原理:

BaseObservable.javaprivate transient PropertyChangeRegistry mCallbacks;public void notifyPropertyChanged(int fieldId) {synchronized (this) {if (mCallbacks == null) {return;}}mCallbacks.notifyCallbacks(this, fieldId, null);
}CallbackRegistry.javapublic synchronized void notifyCallbacks(T sender, int arg, A arg2) {...// sender 是 User// arg 是 BR.namenotifyRecurse(sender, arg, arg2);...
}private void notifyRecurse(T sender, int arg, A arg2) {...// sender 是 User// arg 是 BR.namenotifyRemainder(sender, arg, arg2, remainderIndex);...
}private void notifyRemainder(T sender, int arg, A arg2, int remainderIndex) {if (remainderIndex < 0) {// sender 是 User// arg 是 BR.namenotifyFirst64(sender, arg, arg2);} else {...}
}private void notifyFirst64(T sender, int arg, A arg2) {final int maxNotified = Math.min(Long.SIZE, mCallbacks.size());// sender 是 User// arg 是 BR.namenotifyCallbacks(sender, arg, arg2, 0, maxNotified, mFirst64Removed);
}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) {// sender 是 User// arg 是 BR.name// mCallbacks.get(i) 拿到 BR.name 对应的 WeakPropertyListener// mNotifier 是 PropertyChangeRegistry 的 NOTIFIER_CALLBACKmNotifier.onNotifyCallback(mCallbacks.get(i), sender, arg, arg2);}bitMask <<= 1;}
}

mNotifier 是 PropertyChangeRegistry 通过构造传入的 NOTIFIER_CALLBACK:

CallbackRegistry.javaprivate final NotifierCallback mNotifier;public CallbackRegistry(NotifierCallback notifier) {mNotifier = notifier;
}public class PropertyChangeRegistry extendsCallbackRegistry {private static final CallbackRegistry.NotifierCallback NOTIFIER_CALLBACK = new CallbackRegistry.NotifierCallback() {@Overridepublic void onNotifyCallback(Observable.OnPropertyChangedCallback callback, Observable sender,int arg, Void notUsed) {// callback 是 WeakPropertyListener// sender 是 User// arg 是 BR.namecallback.onPropertyChanged(sender, arg);}};public PropertyChangeRegistry() {super(NOTIFIER_CALLBACK);}
}WeakPropertyListener.javafinal WeakListener mListener;@Override
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 是 ActivityMainBindingImpl// sender 是 User// propertyId 是 BR.namebinder.handleFieldChange(mListener.mLocalFieldId, sender, propertyId);
}ViewDataBinding.javaprivate void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {...// mLocalFieldId = 0// object 是 User // fieldId 是 BR.nameboolean result = onFieldChange(mLocalFieldId, object, fieldId);if (result) {// 等待下一次 VSYNC 信号到来时刷新 UIrequestRebind();}
}ActivityMainBindingImpl.java@Override
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {switch (localFieldId) {case 0 :return onChangeUser((com.example.demo.User) object, fieldId);}return false;
}
private boolean onChangeUser(com.example.demo.User User, int fieldId) {if (fieldId == BR._all) {synchronized(this) {mDirtyFlags |= 0x1L;}return true;}else if (fieldId == BR.name) {synchronized(this) {mDirtyFlags |= 0x2L; // 修改数值,下一个 VSYNC 信号到来时刷新 UI}return true;}return false;
}

根据上面的源码分析,notifyPropertyChanged() 做了以下事情:

  • 根据数据对象绑定 id 从 CallbackRegistry 找到要通知的 WeakPropertyListener

  • WeakPropertyListener.onPropertyChanged() 拿着 WeakListener,WeakListener 对应一个数据对象,再根据 BR.name 找到 mDirtyFlags 修改数值

  • 如果判断 mDirtyFlags 数据需要更新,则等待下一个 VSYNC 信号到来时执行 UI 刷新

具体流程如下:

在这里插入图片描述

总结

该篇文章从布局绑定、注册监听、数据驱动 UI 更新各个流程都做了详细的源码分析,根据上面的内容我们再做对各流程做一个总结梳理。

布局绑定流程 DataBindingUtils.setContentView():

  • 通过 Activity 的 setContentView() 加载布局

  • 根据布局根节点的 tag 属性 layout/activity_xxx_0 创建对应布局的 ViewDataBinding

  • ViewDataBinding 创建时遍历布局所有的 View 存到数组中,数组的 View 赋值给 ViewDataBinding 的控件变量。实现可以通过 binding 获取控件 View

注册监听流程 updateRegistration():

  • 为每一个需要绑定的数据对象提供一个数据对象绑定 id,创建 WeakListener,WeakListener 是 ViewDataBinding、数据对象绑定 id、数据对象 User 的包装类

  • 创建 CallbackRegistry,把它理解为 Observable,将 WeakListener 注册到观察者列表,完成观察者和被观察者的绑定

UI 刷新流程 requestRebind():

DataBinding 刷新 UI 的方式是通过注册编舞者 Choreographer,每 16.6ms 接收一个 VSYNC 信号,再结合 mDirtyFlags 判断数值是否有修改,如果需要更新在下一个 VSYNC 信号到来时刷新 UI

数据驱动 UI 更新流程 notifyPropertyChanged():

  • 根据数据对象绑定 id 从 CallbackRegistry 找到要通知的 WeakPropertyListener

  • WeakPropertyListener.onPropertyChanged() 拿着 WeakListener,WeakListener 对应一个数据对象,再根据 propertyId 找到 mDirtyFlags 修改数值

  • 如果判断 mDirtyFlags 数据需要更新,则等待下一个 VSYNC 信号到来时执行 UI 刷新

我们将所有流程再梳理成伪代码,方便理解:

public class User extends BaseObservable {// PropertyChangeRegistry/CallbackRegitry 本质上就是对这个观察者列表的操作private List mCallbacks;// 注册观察者public void addOnPropertyChangedCallback(WeakListener callback) {mCallbacks.add(callback);}public void setName(String name) {// 数据更新,通知刷新 UInotifyPropertyChanged(BR.name);}public void notifyPropertyChanged(int propertyId) {WeakListener listener = mCallbacks.get(0);int localFieldId = listener.localFieldId;boolean result = listener.binder.onFieldChanged(localFieldId, propertyId);if (result) {// 需要更新 UI,下一个 VSYNC 信号到来时执行 UI 刷新listener.binder.requestRebind();}}
}public class ActivityMainBindingImpl {private User mUser;private int mDirtyFlags;// 注册监听流程public void updateRegistration(User user) {mUser = user;registerTo(0, user);}	public void registerTo(int localFieldId, User user) {WeakListener listener = new WeakListener(this, localFieldId);listener.setTarget(user);}// 收到数据更新需要刷新 UI 通知public boolean onFieldChanged(int localFieldId, int propertyId) {switch (localFieldId) {case 0:if (propertyId == BR.name) {// 修改 mDirtyFlags 数值,下一个 VSYNC 信号刷新 UImDirtyFlags |= 0x2L; return true;}}return false;}// 编舞者刷新 UIpublic void requestRebind() {mChoreographer.postFrameCallback(mFrameCallback);}public ActivityMainBindingImpl() {mChoreographer = Choreographer.getInstance();mFrameCallback = new Choreographer.FrameCallback() {@Overridepublic void doFrame(long frameTimeNanos) {executeBindings();}};}public void executeBindings() {if ((mDirtyFlags & 0x2L) != 0) {textView.setText(mUser.getName());}}
}public class WeakListener {public ViewDataBinding binder;public int localFieldId;public WeakListener(ViewDataBinding binder, int localFieldId) {this.binder = binder;this.localFieldId = localFieldId;}// 注册监听,将观察者和被观察者绑定public void setTarget(User user) {user.addOnPropertyChangedCallback(this);}
}

相关内容

热门资讯

观沧海日月之行赏析   观沧海日月之行赏析  赏析诗歌要知人论世,例如,了解作者生平和诗风,分析写作背景等。  《观沧海...
卞之琳诗歌 卞之琳诗歌  卞之琳(1910-2000),著有诗集《三秋草》(1933)、《鱼目集》(1935)、...
传统过年民俗诗歌介绍 传统过年民俗诗歌介绍  春节是中国最重要最隆重的传统节日。农历正月初一是一年的'开始,接下来小编为你...
建党100周年青春建功新时代... 2021年,我们迎来了建党100周年,新时代的新青年们,请以青春之名、以奋斗之姿建功新时代吧。那建党...
浅语优秀爱情诗歌 浅语优秀爱情诗歌  你说 爱是缘分  可我不曾相见那未知的温柔  我说 情是深浅  可你不曾去寻那隐...
《不醉不归》诗歌 《不醉不归》诗歌  在一个只有猪和草的世界里  黑褐色的狂吃  遮天盖地  子孙后代的身体  瘦得只...
陈毅诗词 陈毅诗词大全  诗词,是指以古体诗、近体诗和格律词为代表的中国古代传统诗歌。亦是汉字文化圈的特色之一...
描写秋天的文艺诗歌大全5篇 在日常学习、工作或生活中,说到诗歌,大家肯定都不陌生吧,诗歌是表现诗人思想感情的一种文学样式。你知道...
现代诗歌金波 现代诗歌金波(精选7首)  在日常生活或是工作学习中,大家一定都接触过一些使用较为普遍的诗歌吧,诗歌...
月亮边的妹妹诗歌 月亮边的妹妹诗歌  遥遥银河边缘  悠悠白云深处  驻守着我的妹妹  美丽善良的妹妹  你是晚霞疲惫...
歌颂劳动者的诗歌朗诵 歌颂劳动者的诗歌朗诵(精选13首)  无论在学习、工作或是生活中,大家都收藏过自己喜欢的诗歌吧,诗歌...
题李凝幽居诗词赏析 题李凝幽居诗词赏析  【诗人简介】  贾岛:(779-843),字阆仙,范阳(今北京)人。早年出家为...
语文诗词的手抄报 关于语文诗词的手抄报  导语:诗词,是指以古体诗、近体诗和格律词为代表的中国古代传统诗歌。亦是汉字文...
对李白《行路难》的赏析 对李白《行路难》的赏析  在日常的学习、工作、生活中,大家都经常接触到诗歌吧,诗歌具有精炼含蓄的特点...
爱情古诗句唯美图片 爱情古诗句唯美图片  不要承诺,不要誓言,只要用一杯茶的温度,品茗一生的幸福。有一种牵挂,在心底反复...
雨霖铃柳永全文及翻译 雨霖铃柳永全文及翻译  《雨霖铃·寒蝉凄切》是宋代词人柳永的词作。此词上片细腻刻画了情人离别的场景,...
林清玄《阳光的味道》全文 林清玄《阳光的味道》全文  林清玄中国著名文化学者,理论家、文化史学家、作家 、散文家。下面是《阳光...
汪藻《春日》原文及译文 汪藻《春日》原文及译文  《春日》是北宋诗人汪藻创作的一首七言律诗。这首诗通过对春日出游的见闻感受的...
于春的诗句 于春的诗句  1) 满目山河空念远,落花风雨更伤春。 ——出处: 晏殊《浣溪沙•一向年光有...
与颜色有关的诗句 与颜色有关的诗句  诗句就是组成的句子。诗句通常按照诗文的格式体例,限定每句字数的多少。以下是小编帮...