DataBinding原理

  • 概述

    Data Binding Library是Jetpack的一部分,旨在把数据绑定的过程和数据通知的逻辑给封装起来,简化开发流程。比如说我们有一个TextView,我们通常在布局文件中设置一个默认值,然后在某个逻辑(比如网络请求)之后动态的去更新它的显示文本,在以前,这一切都需要我们用代码写回调,最后通过setText方法更新显示文本。那么DataBinding的作用呢,就是把这些操作都替我们做了,我们只需要在我们需要的时候去更新数据的值就好了,使用该数据的控件就会自动响应变化进行更新。这里从源码的角度来分析一下是怎么实现的。

    ps:kotlin和ButterKnife等第三方库简化了代码中控件对象和布局文件中控件id的绑定过程,而这里的DataBinding则简化了数据通知控件变化的过程,仔细思考,这些都是和业务逻辑无关的固定流程,我觉得这就是一种启发,对于框架的开发方向的启发。

  • 关于使用

    1. 应用模块(注意是应用module下)的build.gradle文件中加入以下代码并sync gradle:

      android {
              ...
              dataBinding {
                  enabled = true
              }
          }
      
    2. 在布局文件中定义数据并关联到相关组件,比如:

      <?xml version="1.0" encoding="utf-8"?>
          <layout xmlns:android="http://schemas.android.com/apk/res/android">
             <data class="com.xxx.xxx.Xxxx">
                 <import type="com.mph.review.bean.plain.Earth" />
      
                 <import
                  type="com.mph.review.bean.observable.Car"
                  alias="MyCar" />
      
                 <variable
                  name="myCar"
                  type="MyCar" />
                 <variable name="user" type="com.example.User"/>
             
             </data>
             <LinearLayout
                 android:orientation="vertical"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent">
                 <TextView android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:text="@{user.firstName}"/>
                 <TextView android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:text="@{user.lastName}"/>
             </LinearLayout>
          </layout>
      

      注意,最外层是layout标签,和布局根容器同级的是data标签,在data标签中定义数据变量,除了java.lang包下的,也就是基本数据类型之外,其他的类都需要在这里用import标签提前声明,它的alias属性可以声明在此布局文件中你可以使用另外的名字来表示它,或者variable声明的时候type属性用全限定类名值,variable的name属性指定的就是数据变量的名字,通过在代码中给它赋值,在相关控件中引用它。

      如上所示,可以通过data标签的class属性指定生成绑定类的名字和位置,比如:

      以下布局在当前模块的 databinding 包中生成 ContactItem 绑定类:

      <data class="ContactItem">
              …
      </data>
      

      以下示例在模块包中生成绑定类:

      <data class=".ContactItem">
              …
      </data>
      

      以下示例在 com.example 包中创建 ContactItem 绑定类:

      <data class="com.example.ContactItem">
              …
      </data>
      
    3. 代码中赋值:

      erride fun initLogic() {
          val binder : ActivityDemoDatabindBinding = DataBindingUtil.setContentView(this, R.layout.activity_demo_databind)
          //如果使用livedata自动通知数据变化的话则这句一定要加
          binder.lifecycleOwner = this
          binder.handler = LogicHandler(this)
          earth = Earth("Lucas")
          binder.earth = earth
          val car = Car()
          car.name.set("保时捷")
          binder.myCar = car
      
    4. 事件回调绑定

      如果我们要设置监听器方法怎么办呢,比如onClick,传统方式中我们也可以通过onClick属性指定一个含有view参数的方法,这里其实也差不多,我们可以定义一个处理类专门用于处理监听回调,比如:

      //相当于Provider
      class LogicHandler(activity : DemoDataBindActivity) {
      
          private val mActivity = activity
      
          //方法引用方式参数只能和实际事件监听方法完全一致,比如onClick回调监听方法只有一个参数是View,则这里只能有一个参数且必须是View
          fun onChangeEarth(view : View) {
              ToastUtil.showDefaultToast(mActivity, "点击事件-方法引用方式")
          }
      
          //监听器绑定方式参数可以是任意的,而且可以选择是否把原先事件监听方法里的参数传递进来,比如onClick方法,针对下面的这个方法签名,
          // 在布局文件中可以写作(view)->handler.onChangeEarth(view,earth)
          fun onChangeEarth(view : View, earth : Earth) { //布局文件中的earth的name值已经改变但是界面中TextView的值并不会改变
              //            mActivity.earth.name = "Observer"
              //下面这种方式布局文件中的earth的name值也不会改变
              //            mActivity.earth = Earth("Observer")
              ToastUtil.showDefaultToast(mActivity, "点击事件-监听器绑定方式\nearth.name = ${earth.name}")
          }
      
          fun onChangeCar(car : Car) {
              //car.index.set(car.index.get() + 1)
             //下面这样也可以
             mActivity.mCar.index.set(car.index.get() + 1)
          }
      
          fun onChangeLiveDataCar(car : Car) {
              car.liveDataIndex.value = car.liveDataIndex.value?.plus(1)
          }
      }
      

      然后在布局文件中定义处理器类的操作变量,就和其他的数据变量一样定义,加入它的name属性设置成handler,我们在控件中指定onClick属性:

      android:onClick="@{handler::onChangeEarth}"
      

      这样就把LogicHandler类中的onChangeEarth方法绑定到onClick事件上了,注意,和之前一样,方法中必须含有一个View类型的参数。

      除了这种方法引用的方式之外还有一种监听器绑定的方式,这种方式下你不用必须含有View参数,而且可以含有任意数量任意类型的参数,比如:

      android:onClick="@{(theView)->handler.onChangeEarth(theView,earth)}"
      

      或者

      android:onClick="@{()->handler.onChangeCar(myCar)}"
      

      值得注意的是,这里我们使用一个自定义的类来统一处理事件的回调,可以看到它引用了一个Activity的对象,因为它需要弹出一个Toast,通常情况下我们在回调中的操作都会和UI相关,所以这里的LogicHandler类就相当于此Activity的provider,优点就是可以把复杂的逻辑处理封装起来,缺点就是产生了耦合度,要注意防止内存泄露。

    5. 数据类型

      在布局文件中可以绑定普通类型的数据,这种数据只有在调用自动生成的布局Bind类的相关数据变量的set方法的时候界面控件才会改变,只是改变引用的数据时控件并不会自动变化,比如上面的示例中,binder.myCar = car这句代码执行时对应控件才会更新。

      其次,还可以绑定Observeable类型的参数,这里的Observable类是androidx.databinding包下的,绑定这种类型的话,只需要改变这个Observable对象的值(调用set方法更新),引用它的控件就会自动变化,具体类型的子类有ObservableBoolean、ObservableByte、ObservableChar等,如果没有的则可以使用ObservableField<T>,上面LogicHandler的onChangeCar方法中,我们只需要修改数据即可导致控件更新,并不需要重新调用binder的相关方法重新设置。

      还有一种就是引用LiveData类型的数据,比如上面LogicHandler的onChangeLiveDataCar方法中那样。

  • 源码分析

    下面我们通过源码看一下实现原理。

    • 自动化类创建

      首先,gradle中开启databinding之后,build会自动创建相关类,拿上面的DemoDataBindActivity来说,build之后会在build.generated包下的data_binding_base_class_source_out和source.kapt包下生成相关的类:

      image-20210914155926681

      这部分工作是stuido通过执行build自动构建的。

    • 初始化过程

      那么产生了相关类之后,布局中的控件是怎么和数据绑定在一起的呢?

      我们从DataBindingUtil.setContentView开始看:

      public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity,
              int layoutId) {
          return setContentView(activity, layoutId, sDefaultComponent);
      }
      

      sDefaultComponent默认为null,我们现在还不知道他是干啥的,继续往下看:

      public static <T extends ViewDataBinding> 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);
      }
      

      调用activity的setContentView方法把布局文件加载到Activity,然后找到根节点android.R.id.content,走到bindToAddedViews方法,这里面开始就是绑定操作:

      private static <T extends ViewDataBinding> T bindToAddedViews(DataBindingComponent component,
              ViewGroup parent, int startChildren, int layoutId) {
          final int endChildren = parent.getChildCount();
          final int childrenAdded = endChildren - startChildren;
          if (childrenAdded == 1) {
              final View childView = parent.getChildAt(endChildren - 1);
              return bind(component, childView, layoutId);
          } else {
              final View[] children = new View[childrenAdded];
              for (int i = 0; i < childrenAdded; i++) {
                  children[i] = parent.getChildAt(i + startChildren);
              }
              return bind(component, children, layoutId);
          }
      }
      

      这里会找到最后一个View,为什么找最后一个View呢,联想之前xml中的格式,根节点就是layout标签,layout里最开始是data节点,最后一个View就是真正的UI布局节点,这里也能看出,layout标签中是有顺序要求的。该走到bind方法了:

      static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View[] roots,
              int layoutId) {
          return (T) sMapper.getDataBinder(bindingComponent, roots, layoutId);
      }
      

      sMapper是什么?

      private static DataBinderMapper sMapper = new DataBinderMapperImpl();
      

      所以去DataBinderMapperImpl中找一下getDataBinder方法:

      public class DataBinderMapperImpl extends MergedDataBinderMapper {
        DataBinderMapperImpl() {
          addMapper(new com.mph.review.DataBinderMapperImpl());
        }
      }
      

      好吧,这里几乎啥都没有更没有getDataBinder方法,但是注意这里构造方法里有一个addMapper方法,addMapper方法在其父类MergedDataBinderMapper中:

      public void addMapper(DataBinderMapper mapper) {
          Class<? extends DataBinderMapper> mapperClass = mapper.getClass();
          if (mExistingMappers.add(mapperClass)) {
              mMappers.add(mapper);
              final List<DataBinderMapper> dependencies = mapper.collectDependencies();
              for(DataBinderMapper dependency : dependencies) {
                  addMapper(dependency);
              }
          }
      }
      

      mExistingMappers.add(mapperClass)返回true则表示该mapper是第一次添加,则会存在mMappers中,然后会调用mapper的collectDependencies方法,mapper就是前面传过来的com.mph.review.DataBinderMapperImpl实例,所以去看一下这个方法:

      @Override
      public List<DataBinderMapper> collectDependencies() {
        ArrayList<DataBinderMapper> result = new ArrayList<DataBinderMapper>(2);
        result.add(new androidx.databinding.library.baseAdapters.DataBinderMapperImpl());
        result.add(new com.chad.library.DataBinderMapperImpl());
        return result;
      }
      

      回到collectDependencies方法会开启递归,查找这两个类的collectDependencies发现,DataBinderMapperImpl会返回一个空集合,DataBinderMapperImpl会返回一个只含有androidx.databinding.library.baseAdapters.DataBinderMapperImpl实例的集合,所以最后,mMappers中含有的DataBinderMapper有(注意这里是顺序添加的):

      com.mph.review.DataBinderMapperImpl()

      androidx.databinding.library.baseAdapters.DataBinderMapperImpl()

      com.chad.library.DataBinderMapperImpl()

      回到bind方法,现在可以知道getDataBinder方法就在MergedDataBinderMapper中:

      @Override
      public ViewDataBinding getDataBinder(DataBindingComponent bindingComponent, View view,
              int layoutId) {
          for(DataBinderMapper mapper : mMappers) {
              ViewDataBinding result = mapper.getDataBinder(bindingComponent, view, layoutId);
              if (result != null) {
                  return result;
              }
          }
          if (loadFeatures()) {
              return getDataBinder(bindingComponent, view, layoutId);
          }
          return null;
      }
      

      可以看到,这里会把mMappers中的所有DataBinderMapper依次执行getDataBinder方法,所以这个类的名字起名为Mergedxxx,遇到第一个getDataBinder方法返回的ViewDataBinding实例不为null则返回。

      第一个是DataBinderMapperImpl:

      @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_ACTIVITYDEMODATABIND: {
              if ("layout/activity_demo_databind_0".equals(tag)) {
                return new ActivityDemoDatabindBindingImpl(component, view);
              }
              throw new IllegalArgumentException("The tag for activity_demo_databind is invalid. Received: " + tag);
            }
          }
        }
        return null;
      }
      

      DataBinderMapperImpl中有两个getDataBinder方法,就差在第二个参数上,虽然我们的layout标签中有两个子标签(还有一个data),但是最终android.R.id.content里的只有布局标签,data不在之中,想想也对,data也不属于View,所以这里选择这个重载方法。INTERNAL_LAYOUT_ID_LOOKUP是什么呢?

      private static final int LAYOUT_ACTIVITYDEMODATABIND = 1;
      
      private static final SparseIntArray INTERNAL_LAYOUT_ID_LOOKUP = new SparseIntArray(1);
      
      static {
        INTERNAL_LAYOUT_ID_LOOKUP.put(com.mph.review.R.layout.activity_demo_databind, LAYOUT_ACTIVITYDEMODATABIND);
      }
      

      这里的com.mph.review.R.layout.activity_demo_databind就是我自定义的布局xml文件,可见在静态块中INTERNAL_LAYOUT_ID_LOOKUP以布局的资源id为key,value为1保存了起来。

      至于tag是什么时候设置的没找到,应该是inflate的时候通过native方法设置的,然后会返回一个ActivityDemoDatabindBindingImpl对象:

      public ActivityDemoDatabindBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
          this(bindingComponent, root, mapBindings(bindingComponent, root, 9, sIncludes, sViewsWithIds));
      }
      private ActivityDemoDatabindBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
          super(bindingComponent, root, 3
              , (android.widget.Button) bindings[4]
              , (android.widget.Button) bindings[5]
              , (android.widget.Button) bindings[7]
              , (android.widget.Button) bindings[6]
              , (android.widget.TextView) bindings[2]
              , (android.widget.TextView) bindings[8]
              , (android.widget.TextView) bindings[3]
              , (android.widget.TextView) bindings[1]
              );
          this.btnChangeByMethod.setTag(null);
          this.btnChangeByObserver.setTag(null);
          this.btnChangeLivedataValue.setTag(null);
          this.btnChangeObservableValue.setTag(null);
          this.mboundView0 = (androidx.constraintlayout.widget.ConstraintLayout) bindings[0];
          this.mboundView0.setTag(null);
          this.tvCar.setTag(null);
          this.tvLivedataCar.setTag(null);
          this.tvValue.setTag(null);
          setRootTag(root);
          // listeners
          mCallback2 = new com.mph.review.generated.callback.OnClickListener(this, 2);
          mCallback3 = new com.mph.review.generated.callback.OnClickListener(this, 3);
          mCallback1 = new com.mph.review.generated.callback.OnClickListener(this, 1);
          invalidateAll();
      }
      

      super就是findViewById的作用,创建相关view实例。然后就是创建接口回调实例,最后调用invalidateAll方法,其内部调用了requestRebind方法:

      protected void requestRebind() {
          if (mContainingBinding != null) {
              mContainingBinding.requestRebind();
          } else {
              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;
              }
              if (USE_CHOREOGRAPHER) {
                  mChoreographer.postFrameCallback(mFrameCallback);
              } else {
                  mUIThreadHandler.post(mRebindRunnable);
              }
          }
      }
      

      mFrameCallback最终也会调用mRebindRunnable。可以看到,mRebindRunnable会在UI线程执行:

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

      这里只关注两个地方就好,一个是ROOT_REATTACHED_LISTENER:

      private static final OnAttachStateChangeListener ROOT_REATTACHED_LISTENER;
      
      static {
          if (VERSION.SDK_INT < VERSION_CODES.KITKAT) {
              ROOT_REATTACHED_LISTENER = null;
          } else {
              ROOT_REATTACHED_LISTENER = new OnAttachStateChangeListener() {
                  @TargetApi(VERSION_CODES.KITKAT)
                  @Override
                  public void onViewAttachedToWindow(View v) {
                      // execute the pending bindings.
                      final ViewDataBinding binding = getBinding(v);
                      binding.mRebindRunnable.run();
                      v.removeOnAttachStateChangeListener(this);
                  }
      
                  @Override
                  public void onViewDetachedFromWindow(View v) {
                  }
              };
          }
      }
      

      可以看到,给根节点View添加一个监听回调,当关联到窗口时重新走一遍mRebindRunnable,这就是为了执行mRebindRunnable的第二个地方:executePendingBindings()方法。这个方法最终会走到ActivityDemoDatabindBindingImpl的executeBindings方法中,下面是选取的部分代码:

      @Override
      protected void executeBindings() {
          long dirtyFlags = 0;
          synchronized(this) {
              dirtyFlags = mDirtyFlags;
              mDirtyFlags = 0;
          }
          com.mph.review.bean.observable.Car myCar = mMyCar;
          com.mph.review.bean.plain.Earth earth = mEarth;
          if (earth != null) {
                  // read earth.name
              earthName = earth.getName();
          }
          java.lang.String myCarNameMyCarIndex = null;
          androidx.databinding.ObservableInt myCarIndex = null;
              if (myCar != null) {
              // read myCar.index
              myCarIndex = myCar.getIndex();
          }
          updateRegistration(0, myCarIndex);
          if (myCarIndex != null) {
              // read myCar.index.get()
              myCarIndexGet = myCarIndex.get();
          }
          androidx.databinding.ObservableField<java.lang.String> myCarName = null;
          if (myCar != null) {
              // read myCar.name
              myCarName = myCar.getName();
          }
          updateRegistration(1, myCarName);
          if (myCarName != null) {
              // read myCar.name.get()
              myCarNameGet = myCarName.get();
          }
          //因为布局文件中是android:text="@{myCar.name+myCar.index,default = Car}",所以这里把它俩拼在一起然后setText
          myCarNameMyCarIndex = (myCarNameGet) + (myCarIndexGet);
          androidx.databinding.adapters.TextViewBindingAdapter.setText(this.tvCar, myCarNameMyCarIndex);
          this.btnChangeByObserver.setOnClickListener(mCallback1);
      

      可以看到,上面的earthName直接通过getName获取,而myCarIndexGet和myCarNameGet却多了一步get方法,这是因为Car里面的字段是可观察字段,而且还多了一行updateRegistration代码,其实就是注册监听,因为可观察字段包含的内容改变时会自动通知引用它的观察者响应变化,这一点我们放到后面讨论。

      那么到现在为止,整个初始化流程就走完了,然后此时执行的executeBindings其实并没有赋值成功,因为我们还没有给传递值,我们此时只是拿到了生成的ActivityDemoDatabindBindingImpl对象。上文中的initLogic方法中拿到对象之后进行了数据设置工作,通过相关set方法会把数据传递到ActivityDemoDatabindBindingImpl对象中,每次set的时候都会再次调用requestRebind方法,比如:

      public void setMyCar(@Nullable com.mph.review.bean.observable.Car MyCar) {
          this.mMyCar = MyCar;
          synchronized(this) {
              mDirtyFlags |= 0x8L;
          }
          notifyPropertyChanged(BR.myCar);
          super.requestRebind();
      }
      

      executeBindings中又有这样的判断:

      if ((dirtyFlags & 0x4bL) != 0) {
      
              if (myCar != null) {
                  // read myCar.index
                  myCarIndex = myCar.getIndex();
              }
              updateRegistration(0, myCarIndex);
      
      
              if (myCarIndex != null) {
                  // read myCar.index.get()
                  myCarIndexGet = myCarIndex.get();
              }
      }
      

      可以看到,通过与或操作,造成相关View的数据更新操作。

    • 可观察的数据对象

      还记得上面的androidx.databinding.ObservableXxx类型的字段每次取到的时候都要执行一下updateRegistration方法吗,它有三个重载方法:

      /**
       * @hide
       */
      protected boolean updateRegistration(int localFieldId, Observable observable) {
          return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
      }
      
      /**
       * @hide
       */
      protected boolean updateRegistration(int localFieldId, ObservableList observable) {
          return updateRegistration(localFieldId, observable, CREATE_LIST_LISTENER);
      }
      
      /**
       * @hide
       */
      protected boolean updateRegistration(int localFieldId, ObservableMap observable) {
          return updateRegistration(localFieldId, observable, CREATE_MAP_LISTENER);
      }
      

      就是分成三种,Map、List和其他,我们这里只看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();
          }
      };
      

      继续updateRegistration方法:

      private boolean updateRegistration(int localFieldId, Object observable,
              CreateWeakListener listenerCreator) {
          if (observable == null) {
              return unregisterFrom(localFieldId);
          }
          WeakListener listener = mLocalFieldObservers[localFieldId];
          if (listener == null) {
              registerTo(localFieldId, observable, listenerCreator);
              return true;
          }
          if (listener.getTarget() == observable) {
              return false;//nothing to do, same object
          }
          unregisterFrom(localFieldId);
          registerTo(localFieldId, observable, listenerCreator);
          return true;
      }
      

      这里就是缓存的功能,实质上在于registerTo方法:

      protected void registerTo(int localFieldId, Object observable,
              CreateWeakListener listenerCreator) {
          if (observable == null) {
              return;
          }
          WeakListener listener = mLocalFieldObservers[localFieldId];
          if (listener == null) {
              listener = listenerCreator.create(this, localFieldId);
              mLocalFieldObservers[localFieldId] = listener;
              if (mLifecycleOwner != null) {
                  listener.setLifecycleOwner(mLifecycleOwner);
              }
          }
          listener.setTarget(observable);
      }
      

      这里的listenerCreator自然是CREATE_PROPERTY_LISTENER,所以这里create方法创建的listener就是WeakPropertyListener的getListener方法得到的:

      public WeakPropertyListener(ViewDataBinding binder, int localFieldId) {
          mListener = new WeakListener<Observable>(binder, localFieldId, this);
      }
      

      所以listener就是WeakListener,它的第三个参数就是当前的WeakPropertyListener,这个参数作为WeakListener的mObservable的值。

      上面的registerTo做了两个事,一个是setLifecycleOwner:

      public void setLifecycleOwner(LifecycleOwner lifecycleOwner) {
          mObservable.setLifecycleOwner(lifecycleOwner);
      }
      

      可以看到就是调用了WeakPropertyListener的setLifecycleOwner方法,但是WeakPropertyListener里面对这个方法并没有实现任何代码,这里的设计只是为了扩展。

      另一个是setTarget方法:

      public void setTarget(T object) {
          unregister();
          mTarget = object;
          if (mTarget != null) {
              mObservable.addListener(mTarget);
          }
      }
      
      public boolean unregister() {
          boolean unregistered = false;
          if (mTarget != null) {
              mObservable.removeListener(mTarget);
              unregistered = true;
          }
          mTarget = null;
          return unregistered;
      }
      

      这里的mTarget就是设置成传进来的可观察字段,比如ObservableInt,来到WeakPropertyListener的addListener方法:

      @Override
      public void addListener(Observable target) {
          target.addOnPropertyChangedCallback(this);
      }
      

      那以ObservableInt为例,接下来会走到它的addOnPropertyChangedCallback方法,发现这个方法存在于父类BaseObservable里面:

      @Override
      public void addOnPropertyChangedCallback(@NonNull OnPropertyChangedCallback callback) {
          synchronized (this) {
              if (mCallbacks == null) {
                  mCallbacks = new PropertyChangeRegistry();
              }
          }
          mCallbacks.add(callback);
      }
      

      可以看到,这里会在mCallbacks中存入callback,callback也就是前面的WeakPropertyListener,注意这里的target是可观察字段对象,所以是添加到了可观察字段对象的mCallbacks中。到这里,注册的工作就完成了。

      那可观察字段对象的数据改变会响应相关引用它的View进行更新的触发点在哪呢?

      可观察字段对象不像普通字段一样,都有一个set方法,通过它来更新维护的值:

      public void set(int value) {
          if (value != mValue) {
              mValue = value;
              notifyChange();
          }
      }
      

      notifyChange方法如下:

      public void notifyChange() {
          synchronized (this) {
              if (mCallbacks == null) {
                  return;
              }
          }
          mCallbacks.notifyCallbacks(this, 0, null);
      }
      

      我们发现这个方法正是BaseObservable下的方法,那这里的mCallbacks我们就知道是什么了,它的notifyCallbacks方法是:

      public synchronized void notifyCallbacks(T sender, int arg, A arg2) {
          mNotificationLevel++;
          notifyRecurse(sender, arg, arg2);
          mNotificationLevel--;
          if (mNotificationLevel == 0) {
              if (mRemainderRemoved != null) {
                  for (int i = mRemainderRemoved.length - 1; i >= 0; i--) {
                      final long removedBits = mRemainderRemoved[i];
                      if (removedBits != 0) {
                          removeRemovedCallbacks((i + 1) * Long.SIZE, removedBits);
                          mRemainderRemoved[i] = 0;
                      }
                  }
              }
              if (mFirst64Removed != 0) {
                  removeRemovedCallbacks(0, mFirst64Removed);
                  mFirst64Removed = 0;
              }
          }
      }
      

      其他的代码都是清理工作,我们只看notifyRecurse方法,它的里面又调用了notifyCallbacks方法:

      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) {
                  mNotifier.onNotifyCallback(mCallbacks.get(i), sender, arg, arg2);
              }
              bitMask <<= 1;
          }
      }
      

      mNotifier是什么,我们发现它是在CallbackRegistry构造方法里赋值的,CallbackRegistry是PropertyChangeRegistry的父类,所以参数就是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) {
              callback.onPropertyChanged(sender, arg);
          }
      };
      
      public PropertyChangeRegistry() {
          super(NOTIFIER_CALLBACK);
      }
      

      也就是NOTIFIER_CALLBACK,可以看到,它的onNotifyCallback就是执行callback的onPropertyChanged方法,callback又是从mCallbacks中取的,在这里也就是WeakPropertyListener:

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

      handleFieldChange方法最终又会走到ActivityDemoDatabindBindingImpl的onFieldChange中:

      @Override
      protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
          switch (localFieldId) {
              case 0 :
                  return onChangeMyCarIndex((androidx.databinding.ObservableInt) object, fieldId);
              case 1 :
                  return onChangeMyCarName((androidx.databinding.ObservableField<java.lang.String>) object, fieldId);
              case 2 :
                  return onChangeMyCarLiveDataIndex((androidx.lifecycle.MutableLiveData<java.lang.Integer>) object, fieldId);
          }
          return false;
      }
      

      这里有所有的可观察对象的监听逻辑,按照localFieldId一一对应,拿第一个来看:

      private boolean onChangeMyCarIndex(androidx.databinding.ObservableInt MyCarIndex, int fieldId) {
          if (fieldId == BR._all) {
              synchronized(this) {
                      mDirtyFlags |= 0x1L;
              }
              return true;
          }
          return false;
      }
      

      回头查看发现,前面mCallbacks调用notifyCallbacks方法传入的第二个参数0其实就是作为这里的fieldId传入,0恰好是BR._all的值,所以这里会返回true,回到handleFieldChange方法后会因此执行requestRebind方法,又因为修改了mDirtyFlags,从而之针对相关的View刷新数据。

    • LiveData

      LiveData类型的数据的通知流程是一样的,它使用的是LiveDataListener,和前面的不同的是它的setLifecycleOwner方法是实现了的:

      @Override
      public void setLifecycleOwner(LifecycleOwner lifecycleOwner) {
          LifecycleOwner owner = (LifecycleOwner) lifecycleOwner;
          LiveData<?> liveData = mListener.getTarget();
          if (liveData != null) {
              if (mLifecycleOwner != null) {
                  liveData.removeObserver(this);
              }
              if (lifecycleOwner != null) {
                  liveData.observe(owner, this);
              }
          }
          mLifecycleOwner = owner;
      }
      

      可以看到,LiveData最大的不同就是会把当前的LiveDataListener这个Observer和Android的生命周期组件(Activity等)绑定在一起,看一下它的addListener方法:

      @Override
      public void addListener(LiveData<?> target) {
          if (mLifecycleOwner != null) {
              target.observe(mLifecycleOwner, this);
          }
      }
      

      从这两个地方都可以看出来如果没有LifecycleOwner则不会监听数据变化,所以使用LiveData的话,获取到ActivityDemoDatabindBinding实例后一定要设置一下LifecycleOwner:

      val binder : ActivityDemoDatabindBinding = DataBindingUtil.setContentView(this, R.layout.activity_demo_databind)
      //如果使用livedata自动通知数据变化的话则这句一定要加
      binder.lifecycleOwner = this
      

      setXxx的时候会通过registerTo方法传入LiveDataListener。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,841评论 5 472
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,415评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,904评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,051评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,055评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,255评论 1 278
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,729评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,377评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,517评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,420评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,467评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,144评论 3 317
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,735评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,812评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,029评论 1 256
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,528评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,126评论 2 341

推荐阅读更多精彩内容