Jetpack:DataBinding
创始人
2025-06-01 07:01:38
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);}
}

相关内容

热门资讯

安卓子系统windows11,... 你知道吗?最近科技圈可是炸开了锅,因为安卓子系统在Windows 11上的兼容性成了大家热议的话题。...
电脑里怎么下载安卓系统,电脑端... 你有没有想过,你的电脑里也能装上安卓系统呢?没错,就是那个让你手机不离手的安卓!今天,就让我来带你一...
索尼相机魔改安卓系统,魔改系统... 你知道吗?最近在摄影圈里掀起了一股热潮,那就是索尼相机魔改安卓系统。这可不是一般的改装,而是让这些专...
安卓系统哪家的最流畅,安卓系统... 你有没有想过,为什么你的手机有时候像蜗牛一样慢吞吞的,而别人的手机却能像风一样快?这背后,其实就是安...
安卓最新系统4.42,深度解析... 你有没有发现,你的安卓手机最近是不是有点儿不一样了?没错,就是那个一直在默默更新的安卓最新系统4.4...
android和安卓什么系统最... 你有没有想过,你的安卓手机到底是用的是什么系统呢?是不是有时候觉得手机卡顿,运行缓慢,其实跟这个系统...
平板装安卓xp系统好,探索复古... 你有没有想过,把安卓系统装到平板上,再配上XP系统,这会是怎样一番景象呢?想象一边享受着安卓的便捷,...
投影仪装安卓系统,开启智能投影... 你有没有想过,家里的老式投影仪也能焕发第二春呢?没错,就是那个曾经陪你熬夜看电影的“老伙计”,现在它...
安卓系统无线车载carplay... 你有没有想过,开车的时候也能享受到苹果设备的便利呢?没错,就是那个让你在日常生活中离不开的iOS系统...
谷歌安卓8系统包,系统包解析与... 你有没有发现,手机更新换代的速度简直就像坐上了火箭呢?这不,最近谷歌又发布了安卓8系统包,听说这个新...
微软平板下软件安卓系统,开启全... 你有没有想过,在微软平板上也能畅享安卓系统的乐趣呢?没错,这就是今天我要跟你分享的神奇故事。想象你手...
coloros是基于安卓系统吗... 你有没有想过,手机里的那个色彩斑斓的界面,背后其实有着一个有趣的故事呢?没错,我要说的就是Color...
安卓神盾系统应用市场,一站式智... 你有没有发现,手机里的安卓神盾系统应用市场最近可是火得一塌糊涂啊!这不,我就来给你好好扒一扒,看看这...
黑莓平板安卓系统升级,解锁无限... 亲爱的读者们,你是否还记得那个曾经风靡一时的黑莓手机?那个标志性的全键盘,那个独特的黑莓体验,如今它...
安卓文件系统采用华为,探索高效... 你知道吗?最近安卓系统在文件管理上可是有了大动作呢!华为这个科技巨头,竟然悄悄地给安卓文件系统来了个...
深度系统能用安卓app,探索智... 你知道吗?现在科技的发展真是让人惊叹不已!今天,我要给你揭秘一个超级酷炫的话题——深度系统能用安卓a...
安卓系统的分区类型,深度解析存... 你有没有发现,你的安卓手机里藏着不少秘密?没错,就是那些神秘的分区类型。今天,就让我带你一探究竟,揭...
安卓系统铠无法兑换,揭秘无法兑... 最近是不是有很多小伙伴在玩安卓系统的游戏,突然发现了一个让人头疼的问题——铠无法兑换!别急,今天就来...
汽车安卓系统崩溃怎么刷,一键刷... 亲爱的车主朋友们,你是否曾遇到过汽车安卓系统崩溃的尴尬时刻?手机系统崩溃还能重启,但汽车系统崩溃了,...
miui系统可以刷安卓p系统吗... 亲爱的手机控们,你是否对MIUI系统情有独钟,同时又对安卓P系统的新鲜功能垂涎欲滴?今天,就让我带你...