DataBinding 随便写写

悲惨的起源

首先DataBinding已经出现很久了,不过大部分人都不太待见,都选择其他的例如ButterKnife等(不过这东西不更新了哦亲~~),或者原式的findview也好,只能感叹一句生不逢时,而且大部分人也反感在xml里面写逻辑,但是他的双向绑定让我们能视图和数据保持一致不需要去处理很麻烦的set啊等等。

PS:如果你只是想省点findView当我没说,毕竟很多人对双向绑定很反感,不过我比较推荐使用新的ViewBinding,他用起来比Databinding简单而且能高效。

原理

不讲用法了,满大街都是自己去看,还不懂建议百度。

首先了解一件事情,Databinding在使用后会生产以下几个,几个XML,binding.java,BR文件类似R,1个DataBinderMapper

XML

首先讲2个XML

一个XML是去掉了<layout>,<data>的生成的XML,这个就不展示了自己去找,就和我们普通的XML一样,还有一个在/build/intermediates/data_binding_info_type/../../../out/..xml

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Layout bindingClass="MainActivityDataBinding" directory="layout"
    filePath="。。。此处手动修改马赛克\app\src\main\res\layout\activity_main.xml"
    isMerge="false" layout="activity_main" modulePackage="com.example.mydatabindinglearning">
    <ClassNameLocation endLine="5" endOffset="39" startLine="5" startOffset="17" />
    <Variables name="dataBean" declared="true" type="com.example.mydatabindinglearning.DataBean">
        <location endLine="9" endOffset="63" startLine="7" startOffset="8" />
    </Variables>
    <Targets>
        <Target tag="layout/activity_main_0"
            view="androidx.constraintlayout.widget.ConstraintLayout">
            <Expressions />
            <location endLine="25" endOffset="55" startLine="12" startOffset="4" />
        </Target>
        <Target tag="binding_1" view="TextView">
            <Expressions>
                <Expression attribute="android:text" text="dataBean.firstName">
                    <Location endLine="20" endOffset="47" startLine="20" startOffset="12" />
                    <TwoWay>false</TwoWay>
                    <ValueLocation endLine="20" endOffset="45" startLine="20" startOffset="28" />
                </Expression>
            </Expressions>
            <location endLine="24" endOffset="55" startLine="17" startOffset="8" />
        </Target>
    </Targets>
</Layout>

Variables你input的东西,targets就是你之前的view啊什么的,还告知了绑定关系不,Expressions 里面有个text可以看到我绑定了个。

binding

之后是一个binding.java和一个bindingImpl.java
举个例子
MainActivityDataBinding 我的这个在/build/generated/data_binding_base_source_out/下面很多层

public abstract class MainActivityDataBinding extends ViewDataBinding {
   @NonNull
  public final TextView tvText;

  @Bindable
  protected DataBean mDataBean;

  protected MainActivityDataBinding(Object _bindingComponent, View _root, int _localFieldCount,
      TextView tvText) {
    super(_bindingComponent, _root, _localFieldCount);
    this.tvText = tvText;
  }

  public abstract void setDataBean(@Nullable DataBean dataBean);

  @Nullable
  public DataBean getDataBean() {
    return mDataBean;
  }

  @NonNull
  public static MainActivityDataBinding inflate(@NonNull LayoutInflater inflater,
      @Nullable ViewGroup root, boolean attachToRoot) {
    return inflate(inflater, root, attachToRoot, DataBindingUtil.getDefaultComponent());
  }
  //此处有很多的省略 ヾ( ̄ー ̄)X(^▽^)ゞ
}

可从上面看到,有绑定的参,有绑定的ID的VIEW等等,VIEWID的是public的可以直接在外面调用,Binding内部主要负责双向绑定的功能。

还有1个impl.javaandroidX下面

BR

BR文件如果你用了androidX会有2个BR,androidX的databinding单独生成了一个,目录/build/generated/ap_generated_sources/自己点下去

public class BR {
  public static final int _all = 0;

  public static final int dataBean = 1;
}

DataBinderMapperImpl

下面还有很多就不写了,其实就是些映射关系的绑定。

public class DataBinderMapperImpl extends DataBinderMapper {
  private static final int LAYOUT_ACTIVITYMAIN = 1;

  private static final SparseIntArray INTERNAL_LAYOUT_ID_LOOKUP = new SparseIntArray(1);

  static {
    INTERNAL_LAYOUT_ID_LOOKUP.put(com.example.mydatabindinglearning.R.layout.activity_main, LAYOUT_ACTIVITYMAIN);
  }

  @Override
  public ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) {
    int localizedLayoutId = INTERNAL_LAYOUT_ID_LOOKUP.get(layoutId);
    if(localizedLayoutId > 0) {
      final Object tag = view.getTag();
      if(tag == null) {
        throw new RuntimeException("view must have a tag");
      }
      switch(localizedLayoutId) {
        case  LAYOUT_ACTIVITYMAIN: {
          if ("layout/activity_main_0".equals(tag)) {
            return new MainActivityDataBindingImpl(component, view);
          }
          throw new IllegalArgumentException("The tag for activity_main is invalid. Received: " + tag);
        }
      }
    }
    return null;
  }
  //很多的省略
}

以上就是我们build会自动帮我们生成的文件那马下面来看看绑定的过程。

init 初始化

DataBindingUtil.setContentView(this,R.layout....);

这个是最基础的绑定方法我们可以点进去看发现是这样的

   // @Nullable don't annotate with Nullable. It is unlikely to be null and makes using it from
    // kotlin really ugly. We cannot make it NonNull w/o breaking backward compatibility.
    public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity,
            int layoutId, @Nullable DataBindingComponent bindingComponent) {
        activity.setContentView(layoutId);
        View decorView = activity.getWindow().getDecorView();
        ViewGroup contentView = (ViewGroup) decorView.findViewById(android.R.id.content);
        return bindToAddedViews(bindingComponent, contentView, 0, layoutId);
    }

首先他还是调用了setContentView这个方法,其次他有个bindToAddedViews里面有个bind方法,会看到个

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

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

sMapper???这就是值钱我们说的自动生成的类,你可以在里面看到绑定的具体方法。
下面列出代码

@Override
  public ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) {
    int localizedLayoutId = INTERNAL_LAYOUT_ID_LOOKUP.get(layoutId);
    if(localizedLayoutId > 0) {
      final Object tag = view.getTag();
      if(tag == null) {
        throw new RuntimeException("view must have a tag");
      }
      switch(localizedLayoutId) {
        case  LAYOUT_ACTIVITYMAIN: {
          if ("layout/activity_main_0".equals(tag)) {
            return new MainActivityDataBindingImpl(component, view);
          }
          throw new IllegalArgumentException("The tag for activity_main is invalid. Received: " + tag);
        }
      }
    }
    return null;
  }

进入到MainActivityDataBindingImpl里面有个mapBindings,里面代码很多简单来说就是把bindings数组强转为我们的控件类型。

PS:也就是说所有的布局控件都会放到一个数组对象中,那么这个数组对象大小是不定的,如果你有多个activity就会存在多个数组对象,这是比较占用内存的。

回到MainActivityDataBindingImpl的构造函数,之后是invalidateAll >> requestRebind

 /**
     * @hide
     */
    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;
            }
            //USE_CHOREOGRAPHER判断版本的 小于16的走另外一套 不看了,因为。。现在没有小于19的不需反驳
            if (USE_CHOREOGRAPHER) {
                mChoreographer.postFrameCallback(mFrameCallback);
            } else {
                mUIThreadHandler.post(mRebindRunnable);
            }
        }
    }

   /**
     * Runnable executed on animation heartbeat to rebind the dirty Views.
     */
    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();
        }
    };

mUIThreadHandleremmmmmmmm貌似知道了什么,更新都是通过handler通知view进行更新的,handler内部拥有一个Looper对象(这里摆出他的创建语句mUIThreadHandler = new Handler(Looper.myLooper());),是不断的在执行消息循环。

processReferenceQueue是删除监听器的,重点关注executePendingBindings他最后会执行会impl里面的executeBindings,很长反正你也不会看不举例了,直接executeBindings

 @Override
    protected void executeBindings() {
        long dirtyFlags = 0;
        synchronized(this) {
            dirtyFlags = mDirtyFlags;
            mDirtyFlags = 0;
        }
        androidx.databinding.ObservableField<java.lang.String> dataBeanFirstName = null;
        com.example.mydatabindinglearning.DataBean dataBean = mDataBean;
        java.lang.String dataBeanFirstNameGet = null;
        if ((dirtyFlags & 0x7L) != 0) {
                if (dataBean != null) {
                    // read dataBean.firstName
                    dataBeanFirstName = dataBean.firstName;
                }
                updateRegistration(0, dataBeanFirstName);
                if (dataBeanFirstName != null) {
                    // read dataBean.firstName.get()
                    dataBeanFirstNameGet = dataBeanFirstName.get();
                }
        }
        // batch finished
        if ((dirtyFlags & 0x7L) != 0) {
            // api target 1
            androidx.databinding.adapters.TextViewBindingAdapter.setText(this.tvText, dataBeanFirstNameGet);
        }
    }

emmmmmmmmmmm dataBeanFirstName.get获取Model的字段值,然后setText显示出来,破费

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容