DataBinding(含ViewBinding)(二)绑定原理

2,241 阅读10分钟

上篇已经讲了 ViewBindingDataBinding的使用,下面看一下它们之间的关系。

// 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。接下来将分别讲解 ViewBindingDataBinding的原理。其中用到示例在 BindingSample 可以查到

DataBinding 包含三篇博客:

DataBinding(含ViewBinding)(一)使用

DataBinding(三)构建过程分析

一、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的绑定原理

相较 ViewBindingDataBinding则复杂许多,其需要涉及数据绑定。

2.1 生成的代码

BindingSample 示例中的 activity_test5.xml为例,来查看生成的文件:

编译DataBinding生成的代码主要有 activity_test5.xmlactivity_test5-layout.xmlBRDataBindingMapperActivityTest5Binding

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指向的目标视图信息,其中 标签绑定了 tagView

注意,像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="()-&gt;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编码的项目中会生成两个类:ActivityTest5BindingActivityTest5BindingImpl,其中前者是 abstract类,后者是前者的子类。

目录:

  1. Project\module\build\generated\data_binding_base_class_source_out\debug\out\com\zbt\databinding\databinding\ActivityTest5Binding.java
  2. 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接口,所以从这里也可看出ViewBindingDataBinding子集。而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,如:TextViewBindingAdapterImageViewBindingAdapter,其中常用的方法都同注解 @BindingAdapter重新包装了一次。在 XxxXxxBindingImpl中对各个View的设值时通过这些适配器进行转换设值的。

上面的流程是设置data时 model -> view 的变化过程,那么还有data中的数据变化时,model -> view 的变化过程。

当model中的字段使用了 ObservableFieldObservableBoolean等,那么在 XxxXxxBindingImpl.javaexecuteBinding中相应的字段变化时会执行 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();
    }
};

创建 CreateWeakListenerWeakListener

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);
}

其中 observableObservableField

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就是 之前传入的 WeakWeakListenerObservableField,这样就完成了 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._all0
        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中双向绑定的整个流程。数据绑定的过程还是比较复杂的,结合流程图可以看清整个绑定过程。

四、参考文章

DataBinding源码解析