上篇已经讲了 ViewBinding和DataBinding的使用,下面看一下它们之间的关系。
// ViewBinding
> Task :app:dataBindingMergeDependencyArtifactsDebug UP-TO-DATE
> Task :app:dataBindingMergeGenClassesDebug UP-TO-DATE
> Task :app:dataBindingGenBaseClassesDebug UP-TO-DATE
// DataBinding
> Task :databinding:dataBindingMergeDependencyArtifactsDebug UP-TO-DATE
> Task :databinding:dataBindingMergeGenClassesDebug UP-TO-DATE
> Task :databinding:dataBindingGenBaseClassesDebug UP-TO-DATE
> Task :databinding:dataBindingTriggerDebug UP-TO-DATE
从上面task 可以看出 ViewBinding属于 DataBindig。接下来将分别讲解 ViewBinding和 DataBinding的原理。其中用到示例在 BindingSample 可以查到
DataBinding 包含三篇博客:
DataBinding(含ViewBinding)(一)使用
一、ViewBinding的绑定原理
在 build.gradle中配置了如下:
buildFeatures{
viewBinding = true
}
即引入了ViewBinding,AS插件会将 xxx.xml生成 XxxBindig.java代码,在xml文件的根布局中设置 tools:viewBindingIgnore="true"则表示忽略该文件,不生成 XxxBinding.java文件
1.1 生成的代码
生成的 XxxBinding.java在该目录下:
app\build\intermediates\javac\debug\classes\com\zbt\viewbindingtest\databinding
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="noId"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_text" />
</androidx.constraintlayout.widget.ConstraintLayout>
生成的 ActivityMainBinding.java:
public final class ActivityMainBinding implements ViewBinding {
@NonNull
private final ConstraintLayout rootView;
@NonNull
public final TextView tvText;
private ActivityMainBinding(@NonNull ConstraintLayout rootView, @NonNull TextView tvText) {
this.rootView = rootView;
this.tvText = tvText;
}
@Override
@NonNull
public ConstraintLayout getRoot() {
return rootView;
}
@NonNull
public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater) {
return inflate(inflater, null, false);
}
@NonNull
public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater,
@Nullable ViewGroup parent, boolean attachToParent) {
// 1.通过布局填充器获取rootView,组合View需要传递的Parent等参数
View root = inflater.inflate(R.layout.activity_main, parent, false);
if (attachToParent) {
parent.addView(root);
}
return bind(root);
}
@NonNull
public static ActivityMainBinding bind(@NonNull View rootView) {
// The body of this method is generated in a way you would not otherwise write.
// This is done to optimize the compiled bytecode for size and performance.
// 2.通过rootView.findViewById 获取到相应的View
int id;
missingId: {//不知道这个是什么
id = R.id.tv_text;
TextView tvText = rootView.findViewById(id);
if (tvText == null) {
break missingId;
}
// 3.将所有查找的View,通过构造方法传递给ActivityMainBinding
return new ActivityMainBinding((ConstraintLayout) rootView, tvText);
}
String missingId = rootView.getResources().getResourceName(id);
throw new NullPointerException("Missing required view with ID: ".concat(missingId));
}
}
可以看到 ActivityMainBinding 实现了 ViewBinding接口,提供 getRoot()返回的是 xml文件的根布局。获取ActivityMainBinding流程如下:
graph TD
ActivityMainBinding.inflate --> ActivityMainBinding.bind --> ActivityMainBinding构造方法
其中,在xml文件中没有设置 id的View是不会在 Binding文件中生成View。
二、DataBinding的绑定原理
相较 ViewBinding,DataBinding则复杂许多,其需要涉及数据绑定。
2.1 生成的代码
以 BindingSample 示例中的 activity_test5.xml为例,来查看生成的文件:
编译DataBinding生成的代码主要有 activity_test5.xml、activity_test5-layout.xml、BR、DataBindingMapper、ActivityTest5Binding。
activity_test5.xml
目录: Project\module\build\intermediates\incremental\mergeDebugResources\stripped.dir\layout\activity_test5.xml
该文件是编写的 activity_test5.xml去除 layout标签的布局文件,其中对每个控件新增了一个 tag,如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="20dp"
android:orientation="vertical"
android:tag="layout/activity_test5_0">
<TextView
style="@style/TvStyle"
android:text="model - View : 双向数据绑定" />
<TextView
style="@style/TvStyle"
android:tag="binding_1" />
<EditText
style="@style/EtStyle"
android:layout_marginTop="10dp"
android:tag="binding_2" />
<TextView
style="@style/TvStyle"
android:layout_marginTop="60dp"
android:text="事件绑定" />
<TextView
style="@style/TvStyle"
android:tag="binding_3" />
<EditText
style="@style/EtStyle"
android:layout_marginTop="10dp"
android:tag="binding_4" />
</LinearLayout>
actiivty_test5-layout.xml
目录:Project\module\build\intermediates\data_binding_layout_info_type_merge\debug\out\actiivty_test5-layout.xml
该文件是根据标签 layout生成的,对activity_test5.xml进行了扩展,其中主要标签如下:
- 包含xml地址、包名、根节点信息。
- model信息
- model指向的目标视图信息,其中 标签绑定了
tag和View
注意,像xml文件中<fragment />这样的标签是不会被成到xxx_xxx-layout.xml文件中,当然在 XxxXxxBinding.java中也不会生成,需要自己查找。
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Layout directory="layout" filePath="databinding\src\main\res\layout\activity_test5.xml"
isBindingData="true" isMerge="false" layout="activity_test5"
modulePackage="com.zbt.databinding" rootNodeType="android.widget.LinearLayout">
<Variables name="workBean" declared="true" type="com.zbt.databinding.ObservableWorkBean">
<location endLine="7" endOffset="59" startLine="5" startOffset="8" />
</Variables>
<Variables name="eventBinding" declared="true"
type="com.zbt.databinding.Test5Activity.EventBinding">
<location endLine="11" endOffset="67" startLine="9" startOffset="8" />
</Variables>
<Targets>
<Target tag="layout/activity_test5_0" view="LinearLayout">
<Expressions />
<location endLine="49" endOffset="18" startLine="14" startOffset="4" />
</Target>
<Target tag="binding_1" view="TextView">
<Expressions>
<Expression attribute="android:text" text="workBean.name">
<Location endLine="26" endOffset="42" startLine="26" startOffset="12" />
<TwoWay>false</TwoWay>
<ValueLocation endLine="26" endOffset="40" startLine="26" startOffset="28" />
</Expression>
</Expressions>
<location endLine="26" endOffset="45" startLine="24" startOffset="8" />
</Target>
<Target tag="binding_2" view="EditText">
<Expressions>
<Expression attribute="android:text" text="workBean.name">
<Location endLine="31" endOffset="43" startLine="31" startOffset="12" />
<TwoWay>true</TwoWay>
<ValueLocation endLine="31" endOffset="41" startLine="31" startOffset="29" />
</Expression>
</Expressions>
<location endLine="31" endOffset="46" startLine="28" startOffset="8" />
</Target>
<Target tag="binding_3" view="TextView">
<Expressions>
<Expression attribute="android:onClick"
text="()->eventBinding.onNameClick(workBean)">
<Location endLine="40" endOffset="70" startLine="40" startOffset="12" />
<TwoWay>false</TwoWay>
<ValueLocation endLine="40" endOffset="68" startLine="40" startOffset="31" />
</Expression>
<Expression attribute="android:text" text="workBean.name">
<Location endLine="41" endOffset="42" startLine="41" startOffset="12" />
<TwoWay>false</TwoWay>
<ValueLocation endLine="41" endOffset="40" startLine="41" startOffset="28" />
</Expression>
</Expressions>
<location endLine="41" endOffset="45" startLine="38" startOffset="8" />
</Target>
<Target tag="binding_4" view="EditText">
<Expressions>
<Expression attribute="android:afterTextChanged"
text="eventBinding::onAfterTextChanged">
<Location endLine="46" endOffset="73" startLine="46" startOffset="12" />
<TwoWay>false</TwoWay>
<ValueLocation endLine="46" endOffset="71" startLine="46" startOffset="40" />
</Expression>
<Expression attribute="android:hint" text="workBean.name">
<Location endLine="47" endOffset="42" startLine="47" startOffset="12" />
<TwoWay>false</TwoWay>
<ValueLocation endLine="47" endOffset="40" startLine="47" startOffset="28" />
</Expression>
</Expressions>
<location endLine="47" endOffset="45" startLine="43" startOffset="8" />
</Target>
</Targets>
</Layout>
BR
目录:Project\module\build\generated\source\kapt\debug\com\zbt\databinding\BR.java
该目录主要根据<Variable>和用 @Bindable注解的字段生成id,类似如资源生成的id,ResId。
public class BR {
public static final int _all = 0;
public static final int activity = 1;
public static final int employeeBean = 4;
public static final int eventBinding = 5;
……
public static final int workBean = 13;
public static final int workContent = 14;
public static final int workName = 15;
}
DataBindingmapperImpl
目录:Project\module\build\generated\source\kapt\debug\com\zbt\databinding\DataBindingmapperImpl.java
该文件主要是存储 tag绑定对应的 xml,以及BR中的ID与对应的字段
public class DataBinderMapperImpl extends DataBinderMapper {
private static final int LAYOUT_ACTIVITYTEST1 = 1;
private static final SparseIntArray INTERNAL_LAYOUT_ID_LOOKUP = new SparseIntArray(13);
static {
INTERNAL_LAYOUT_ID_LOOKUP.put(com.zbt.databinding.R.layout.activity_test1,
……
INTERNAL_LAYOUT_ID_LOOKUP.put(com.zbt.databinding.R.layout.view_stub, LAYOUT_VIEWSTUB);
}
@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_ACTIVITYTEST1: {
if ("layout/activity_test1_0".equals(tag)) {
return new ActivityTest1BindingImpl(component, view);
}
throw new IllegalArgumentException("The tag for activity_test1 is invalid. Received: " + tag);
}
……
}
}
return null;
}
@Override
public ViewDataBinding getDataBinder(DataBindingComponent component, View[] views, int layoutId) {
if(views == null || views.length == 0) {
return null;
}
int localizedLayoutId = INTERNAL_LAYOUT_ID_LOOKUP.get(layoutId);
if(localizedLayoutId > 0) {
final Object tag = views[0].getTag();
if(tag == null) {
throw new RuntimeException("view must have a tag");
}
switch(localizedLayoutId) {
}
}
return null;
}
@Override
public int getLayoutId(String tag) {
if (tag == null) {
return 0;
}
Integer tmpVal = InnerLayoutIdLookup.sKeys.get(tag);
return tmpVal == null ? 0 : tmpVal;
}
@Override
public String convertBrIdToString(int localId) {
String tmpVal = InnerBrLookup.sKeys.get(localId);
return tmpVal;
}
@Override
public List<DataBinderMapper> collectDependencies() {
ArrayList<DataBinderMapper> result = new ArrayList<DataBinderMapper>(1);
result.add(new androidx.databinding.library.baseAdapters.DataBinderMapperImpl());
return result;
}
private static class InnerBrLookup {
static final SparseArray<String> sKeys = new SparseArray<String>(16);
static {
sKeys.put(0, "_all");
……
}
}
private static class InnerLayoutIdLookup {
static final HashMap<String, Integer> sKeys = new HashMap<String, Integer>(13);
static {
sKeys.put("layout/activity_test1_0", com.zbt.databinding.R.layout.activity_test1);
……
}
}
}
Activity5TestBinding
kotlin编码的项目中会生成两个类:ActivityTest5Binding和ActivityTest5BindingImpl,其中前者是 abstract类,后者是前者的子类。
目录:
- Project\module\build\generated\data_binding_base_class_source_out\debug\out\com\zbt\databinding\databinding\ActivityTest5Binding.java
- Project\module\build\generated\source\kapt\debug\com\zbt\databinding\databinding\ActivityTest5BindingImpl.java
简单分析看
// ViewDataBinding
public abstract class ViewDataBinding extends BaseObservable implements ViewBinding {}
// ActivityTest5Binding
public abstract class ActivityTest5Binding extends ViewDataBinding {
@Bindable
protected ObservableWorkBean mWorkBean;
@Bindable
protected Test5Activity.EventBinding mEventBinding;
protected ActivityTest5Binding(Object _bindingComponent, View _root, int _localFieldCount) {
super(_bindingComponent, _root, _localFieldCount);
}
public abstract void setWorkBean(@Nullable ObservableWorkBean workBean);
@Nullable
public ObservableWorkBean getWorkBean() {
return mWorkBean;
}
……
}
// ActivityTest5BindingImpl
public class ActivityTest5BindingImpl extends ActivityTest5Binding implements com.zbt.databinding.generated.callback.OnClickListener.Listener {
@NonNull
private final android.widget.LinearLayout mboundView0;
@NonNull
private final android.widget.TextView mboundView1;
@NonNull
private final android.widget.EditText mboundView2;
@NonNull
private final android.widget.TextView mboundView3;
@NonNull
private final android.widget.EditText mboundView4;
public ActivityTest5BindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
this(bindingComponent, root, mapBindings(bindingComponent, root, 5, sIncludes, sViewsWithIds));
}
private ActivityTest5BindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
super(bindingComponent, root, 1
);
// 设置 View的方法和ViewBinding中类似都是在构造方法中赋值,如果是View设置了Id,则不会再这里赋值,会在ActivityTest5Binding构造函数中对生成的View进行赋值
this.mboundView0 = (android.widget.LinearLayout) bindings[0];
this.mboundView0.setTag(null);
this.mboundView1 = (android.widget.TextView) bindings[1];
this.mboundView1.setTag(null);
this.mboundView2 = (android.widget.EditText) bindings[2];
this.mboundView2.setTag(null);
this.mboundView3 = (android.widget.TextView) bindings[3];
this.mboundView3.setTag(null);
this.mboundView4 = (android.widget.EditText) bindings[4];
this.mboundView4.setTag(null);
setRootTag(root);
// listeners
mCallback1 = new com.zbt.databinding.generated.callback.OnClickListener(this, 1);
invalidateAll();
}
}
可以看到 ViewDataBinding实现了ViewBinding接口,所以从这里也可看出ViewBinding是DataBinding子集。而XxxBindingImpl文件主要是生成相应的 View及对 View进行数据绑定。
其中 mapBindings()方法是获取布局中的View
private static void mapBindings(DataBindingComponent bindingComponent, View view,
Object[] bindings, IncludedLayouts includes, SparseIntArray viewsWithIds,
boolean isRoot) {
……
Object objTag = view.getTag();
final String tag = (objTag instanceof String) ? (String) objTag : null;
boolean isBound = false;
// 根据android:tag="layout/xxx_xxx_0" 是否是根View
if (isRoot && tag != null && tag.startsWith("layout")) {
final int underscoreIndex = tag.lastIndexOf('_');
if (underscoreIndex > 0 && isNumeric(tag, underscoreIndex + 1)) {
final int index = parseTagInt(tag, underscoreIndex + 1);
if (bindings[index] == null) {
bindings[index] = view;
}
indexInIncludes = includes == null ? -1 : index;
isBound = true;
} else {
indexInIncludes = -1;
}
}
……
// 遍历子View
if (view instanceof ViewGroup) {
final ViewGroup viewGroup = (ViewGroup) view;
final int count = viewGroup.getChildCount();
int minInclude = 0;
for (int i = 0; i < count; i++) {
final View child = viewGroup.getChildAt(i);
// 根据tag将 子View放如 bindings数组中
}
}
}
2.2 绑定过程
不同类型的组件使用的绑定方法不一样,分为两种类型如下:
// Activity
DataBindingUtil.setContentView(Activity activity, int layoutId)
// Fragment、View
DataBindingUtil.inflate(LayoutInflater inflater, int layoutId, ViewGroup parent, boolean attachToParent)
看一下调用流程:
graph TD
DataBindingUtil.setContentView --> DataBindingUtil.setContentView1 --> DataBindingUtil.bindToAddedViews --> DataBingdingUtil.bind --> DataBinderMapper.getDataBinder
DataBindingUtil.inflate --> DataBindingUtil.inflate1 -->temp{useChildren}
temp --> |Yes| DataBindingUtil.bindToAddedViews
temp --> |No| DataBingdingUtil.bind
结合上一节生成的文件,可以清晰的知道绑定流程。
2.3 设置数据
设置数据到数据刷新的流程如下:
graph TD
ActivityTest5BindingImpl.setWorkBean --> ViewDataBinding.requestBind --> temp{api>=16}
temp --> |Yes| mChoreographer.postFrameCallback --> mRebindRunnable.run
temp --> |No| mUIThreadHandler.post --> mRebindRunnable.run
mRebindRunnable.run --> ViewDataBinding.executePendingBindings --> ViewDataBinding.executeBindings
依次看下代码:
public void setWorkBean(@Nullable com.zbt.databinding.ObservableWorkBean WorkBean) {
this.mWorkBean = WorkBean;
synchronized (this) {
mDirtyFlags |= 0x4L;
}
notifyPropertyChanged(BR.workBean);
super.requestRebind();
}
mDirtyFlags |= 0x4L 是标志哪个属性变了,notifyPropertyChanged(BR.workBean)通知对应的model有变化。
protected void requestRebind() {
// 使用include时候,mContentingBinding是父类的DataBinding
if (mContainingBinding != null) {
mContainingBinding.requestRebind();
} else {
// 判断是否设置了lifecycle,如果设置LifecycleOwner,则现在不刷新,等到start时,执行executePendingBindings()刷新
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;
}
// api>=16的则采用 choreographer
if (USE_CHOREOGRAPHER) {
mChoreographer.postFrameCallback(mFrameCallback);
} else {
mUIThreadHandler.post(mRebindRunnable);
}
}
}
api>=16s时,会往 mChoreographer发一个 mFrameCallback,否则直接往 UI线程发一个 mRebindRunnable,两者最后都会调用 mRebindRunnable.run()。
private final Runnable mRebindRunnable = new Runnable() {
@Override
public void run() {
synchronized (this) {
mPendingRebind = false;
}
processReferenceQueue();
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
// Nested so that we don't get a lint warning in IntelliJ
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();
}
};
重置 mPendingRebind为false。如果api >= 19,判断 mRoot是否 attach到window上,如果没有的话,则对该 view添加 OnAttachStateChangeListener, 当 根Viewattach到 contentView会回调 onViewAttachedToWindow, 最终都会执行 executePendingBindings(),继而调用executeBindingsInternal()方法。
private void executeBindingsInternal() {
// 正在执行executeBindingsInternal 正在执行赋值,则重新刷新
if (mIsExecutingPendingBindings) {
requestRebind();
return;
}
// hasPendingBindings() == false 表示数据完成刷新,没有数据更改
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;
}
其中 mRebindCallbacks是注册了 addOnRebindCallback(object : OnRebindCallback<ActivityTest5Binding>() 后可以监听每次数据变化后的绑定。最后执行 executeBindbings(),来完成view对数据的设置。
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized (this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
com.zbt.databinding.Test5Activity.EventBinding eventBinding = mEventBinding;
androidx.databinding.adapters.TextViewBindingAdapter.AfterTextChanged afterTextChanged = null;
java.lang.String workBeanNameGet = null;
androidx.databinding.ObservableField<java.lang.String> workBeanName = null;
com.zbt.databinding.ObservableWorkBean workBean = mWorkBean;
if ((dirtyFlags & 0xaL) != 0) {
if (eventBinding != null) {
// read eventBinding::onAfterTextChanged
afterTextChanged = (((mAfterTextChanged == null) ? (mAfterTextChanged = new AfterTextChangedImpl()) : mAfterTextChanged).setValue(eventBinding));
}
}
if ((dirtyFlags & 0xdL) != 0) {
if (workBean != null) {
// read workBean.name
workBeanName = workBean.getName();
}
updateRegistration(0, workBeanName);
if (workBeanName != null) {
// read workBean.name.get()
workBeanNameGet = workBeanName.get();
}
}
// batch finished
if ((dirtyFlags & 0xdL) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView1, workBeanNameGet);
this.mboundView4.setHint(workBeanNameGet);
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.tvBinding3, workBeanNameGet);
}
if ((dirtyFlags & 0x8L) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.mboundView2, null, null, null, mboundView2androidTextAttrChanged);
this.tvBinding3.setOnClickListener(mCallback1);
}
}
该段代码主要是判断 dirtyFlags是否有变化,对应的哪个字段变化了,会对相应变化的字段进行赋值。该字段在 setWorkBean中有讲到。 其中 TextView.setText(),则是通过 TextViewBindingAdapter.setText()进行的赋值。在 databinding-adapters中有对基础的View转换的 BindingAdapter,如:TextViewBindingAdapter,ImageViewBindingAdapter,其中常用的方法都同注解 @BindingAdapter重新包装了一次。在 XxxXxxBindingImpl中对各个View的设值时通过这些适配器进行转换设值的。
上面的流程是设置data时 model -> view 的变化过程,那么还有data中的数据变化时,model -> view 的变化过程。
当model中的字段使用了 ObservableField、ObservableBoolean等,那么在 XxxXxxBindingImpl.java中 executeBinding中相应的字段变化时会执行 updateRegistration(0, workBeanName),该方法是用来注册数据变化监听的,流程如下:
graph TD
XxxBindingImpl.updateRegistration --> ViewDataBinding.updateRegistration --> ViewDataBinding.registerTo --> WeakListener.setTarget --> mObservable.addListener --> WeakPropertyListener.addListener --> target.addOnPropertyChangedCallback
ObservableField.set --> BaseObservable.notifyChange --> CallbackRegistry.notifyCallbacks -->
WeakPropertyListener.onPropertyChanged --> ViewDataBinding.handleFieldChange --> ViewDataBinding.requestRebind
通过上面的流程图可以看出,字段 ObservableField最终添加了 addOnPropertyChangedCallback,而该callback则是 WeakPropertyListener 的实例。 当 ObservableField调用 set(value)则最终调用自己注册的 callback,然后调用 requestRebind实现数据刷新。下面看一下代码:
protected boolean updateRegistration(int localFieldId, Observable observable) {
return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
}
其中 CREATE_PROPERTY_LISTENER:
private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
@Override
public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();
}
};
创建 CreateWeakListener和 WeakListener
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);
}
其中 observable是 ObservableField
private static class WeakListener<T> extends WeakReference<ViewDataBinding> {
public void setTarget(T object) {
unregister();
mTarget = object;
if (mTarget != null) {
mObservable.addListener(mTarget);
}
}
}
其中 mObservable是构造方法中传入的 WeakPropertyListener
private static class WeakPropertyListener extends Observable.OnPropertyChangedCallback
implements ObservableReference<Observable> {
final WeakListener<Observable> mListener;
public WeakPropertyListener(ViewDataBinding binder, int localFieldId) {
mListener = new WeakListener<Observable>(binder, localFieldId, this);
}
@Override
public void addListener(Observable target) {
target.addOnPropertyChangedCallback(this);
}
@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.handleFieldChange(mListener.mLocalFieldId, sender, propertyId);
}
}
addListener(Observable target)中的target就是 之前传入的 WeakWeakListener中 ObservableField,这样就完成了 addOnPropertyChangedCallback。
看一下注册及通知流程
public class BaseObservable implements Observable {
private transient PropertyChangeRegistry mCallbacks;
……
// 1.注册回调监听,创建PropertyChangeRegistry
@Override
public void addOnPropertyChangedCallback(@NonNull OnPropertyChangedCallback callback) {
synchronized (this) {
if (mCallbacks == null) {
mCallbacks = new PropertyChangeRegistry();
}
}
mCallbacks.add(callback);
}
……
public void notifyChange() {
synchronized (this) {
if (mCallbacks == null) {
return;
}
}
mCallbacks.notifyCallbacks(this, 0, null);
}
……
}
public class PropertyChangeRegistry extends
CallbackRegistry<Observable.OnPropertyChangedCallback, Observable, Void> {
// 0.生成的静态变量, 在创建PropertyChangeRegistry时,将其赋值给父类
private static final CallbackRegistry.NotifierCallback<Observable.OnPropertyChangedCallback, Observable, Void> NOTIFIER_CALLBACK = new CallbackRegistry.NotifierCallback<Observable.OnPropertyChangedCallback, Observable, Void>() {
@Override
public void onNotifyCallback(Observable.OnPropertyChangedCallback callback, Observable sender,
int arg, Void notUsed) {
// 4. 最终调用WeakPropertyListener中的方法(基于data改变刷新View的流程)
callback.onPropertyChanged(sender, arg);
}
};
public PropertyChangeRegistry() {
super(NOTIFIER_CALLBACK);
}
……
}
public class CallbackRegistry<C, T, A> implements Cloneable {
private final NotifierCallback<C, T, A> mNotifier;
public CallbackRegistry(NotifierCallback<C, T, A> notifier) {
mNotifier = notifier;
}
// 2.当ObservableField set(value)时,调用notifyCallback
public synchronized void notifyCallbacks(T sender, int arg, A arg2) {
mNotificationLevel++;
notifyRecurse(sender, arg, arg2);
……
}
private void notifyRecurse(T sender, int arg, A arg2) {
notifyRemainder(sender, arg, arg2, remainderIndex);
……
}
private void notifyRemainder(T sender, int arg, A arg2, int remainderIndex) {
if (remainderIndex < 0) {
notifyFirst64(sender, arg, arg2);
} else {
……
notifyCallbacks(sender, arg, arg2, startIndex, endIndex, bits);
}
}
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) {
// 3.mNotifier就是PropertyChangeRegistry的变量NOTIFIER_CALLBACK,然后最终调用callback.onPropertyChanged(sender, arg);
mNotifier.onNotifyCallback(mCallbacks.get(i), sender, arg, arg2);
}
bitMask <<= 1;
}
}
……
public abstract static class NotifierCallback<C, T, A> {
public abstract void onNotifyCallback(C callback, T sender, int arg, A arg2);
}
}
注册通知流程如上面的 0、1、2、3、4五个步骤。
看一下,收到通知后,执行change的逻辑
private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
// 设置了LiveDataRegisterObserver
if (mInLiveDataRegisterObserver) {
// We're in LiveData 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;
}
// 判断文件是否有改变,用fiedId判断,在updateRegistration(0, xxx);赋值时id给的是0,所以会走到刷新
boolean result = onFieldChange(mLocalFieldId, object, fieldId);
if (result) {
requestRebind();
}
}
@Override
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
switch (localFieldId) {
case 0 :
return onChangeWorkBeanName((androidx.databinding.ObservableField<java.lang.String>) object, fieldId);
}
return false;
}
private boolean onChangeWorkBeanName(androidx.databinding.ObservableField<java.lang.String> WorkBeanName, int fieldId) {
if (fieldId == BR._all) { BR._all 为0
synchronized(this) {
mDirtyFlags |= 0x1L;
}
return true;
}
return false;
}
这样当model中有数据变化进而通知ViewB变化的流程已经很清晰了。
View变化通知Model
这种情况一般用于 EditText,当EditText有变化则通知 model,具体如下
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="workBean"
type="com.zbt.databinding.ObservableWorkBean" />
<variable
name="eventBinding"
type="com.zbt.databinding.Test5Activity.EventBinding" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="20dp"
android:orientation="vertical">
<EditText
style="@style/EtStyle"
android:layout_marginTop="10dp"
android:text="@={workBean.name}" />
<EditText
style="@style/EtStyle"
android:layout_marginTop="10dp"
android:afterTextChanged="@{eventBinding::onAfterTextChanged}"
android:hint="@{workBean.name}" />
</LinearLayout>
</layout>
布局中有两个 EditText,区别在于监听的方法不一样,一个是用 android:afterTextChanged,另一个是在 android:text
@Override
protected void executeBindings() {
if ((dirtyFlags & 0x8L) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.mboundView2, null, null, null, mboundView2androidTextAttrChanged);
this.tvBinding3.setOnClickListener(mCallback1);
}
if ((dirtyFlags & 0xaL) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.mboundView4, null, null, afterTextChanged, null);
}
}
可以看到调用了DataBinding的适配器类中的 setTextWatcher设置观察者。其中的变量 mboundView2androidTextAttrChanged是一个接口实现类,如下:
private androidx.databinding.InverseBindingListener mboundView2androidTextAttrChanged = new androidx.databinding.InverseBindingListener() {
@Override
public void onChange() {
java.lang.String callbackArg_0 = androidx.databinding.adapters.TextViewBindingAdapter.getTextString(mboundView2);
……
if (workBeanJavaLangObjectNull) {
……
if (workBeanNameJavaLangObjectNull) {
workBeanName.set(((java.lang.String) (callbackArg_0)));
}
}
}
};
其在 TextViewBindingAdapter.setTextWatcher()中设置
@BindingAdapter(value = {"android:beforeTextChanged", "android:onTextChanged",
"android:afterTextChanged", "android:textAttrChanged"}, requireAll = false)
public static void setTextWatcher(TextView view, final BeforeTextChanged before,
final OnTextChanged on, final AfterTextChanged after,
final InverseBindingListener textAttrChanged) {
final TextWatcher newValue;
if (before == null && after == null && on == null && textAttrChanged == null) {
newValue = null;
} else {
newValue = new TextWatcher() {
……
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (on != null) {
on.onTextChanged(s, start, before, count);
}
if (textAttrChanged != null) {
textAttrChanged.onChange();
}
}
};
}
……
}
其中 textAttrChanged就是上面的变量 mboundView2androidTextAttrChanged。在文本改变后,通知 model 改变。afterTextChanged则要实现 TextViewBindingAdapter中的内部接口
public static class AfterTextChangedImpl implements androidx.databinding.adapters.TextViewBindingAdapter.AfterTextChanged{
private com.zbt.databinding.Test5Activity.EventBinding value;
public AfterTextChangedImpl setValue(com.zbt.databinding.Test5Activity.EventBinding value) {
this.value = value;
return value == null ? null : this;
}
@Override
public void afterTextChanged(android.text.Editable arg0) {
this.value.onAfterTextChanged(arg0);
}
}
同样实现了 view 变化通知 model 赋值
对于 View或ViewGroup,如果其有类似于 EditText的数据改变,或者 ViewGroup中的 onAnimationStart都是可以实现 view -> model这一过程。
三、总结
到此已经将完了,ViewBinding和DataBinding生成的文件,以及DataBinding中双向绑定的整个流程。数据绑定的过程还是比较复杂的,结合流程图可以看清整个绑定过程。