Jetpack - DataBinding 入门与源码分析

235 阅读9分钟

介绍

传统的应用开发中,布局文件通常只负责应用界面的布局工作,如果需要实现页面交互就需要调用setContentView()将Activity、fragment和XML布局文件关联起来。然后通过控件的id找到控件,接着在页面中通过代码对控件进行逻辑处理。页面承担了大部分的工作量,大量的逻辑处理需要在Activity、Fragment中进行处理,因此页面显得臃肿不堪,维护起来也很困难,为了减轻页面的工作量,Google提出了DataBiiding(视图绑定)

DataBinding的主要特点
  • 代码更加简洁,可读性高,能够在XML文件中实现UI控制。
  • 不再需要findViewById操作,
  • 能够与Model实体类直接绑定,易于维护和扩展。

基本使用

开启viewBinding

视图绑定功能可按模块启用,要在某个模块中启用视图绑定,请将 viewBinding 元素添加到build.gradle 文件中

android {
        ...
        viewBinding {
            enabled = true
        }
    }

开启之后在依赖中会多四个aar

image.png

修改布局文件

按住【Alt + 回车键】然后选择 【Convert to data binding layout】也可以生成 DataBinding 需要的布局

<?xml version="1.0" encoding="utf-8"?>
<layout 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">

    <data>

    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <TextView
            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" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

重新编译项目DataBinding库就会生成对应的Binding类,该类用来实现XML布局文件与Model类的绑定

image.png

public class ActivityMainBindingImpl extends ActivityMainBinding  {

    @Nullable
    private static final androidx.databinding.ViewDataBinding.IncludedLayouts sIncludes;
    @Nullable
    private static final android.util.SparseIntArray sViewsWithIds;
    static {
        sIncludes = null;
        sViewsWithIds = null;
    }
    // views
    @NonNull
    private final androidx.constraintlayout.widget.ConstraintLayout mboundView0;
  
    public ActivityMainBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
        this(bindingComponent, root, mapBindings(bindingComponent, root, 1, sIncludes, sViewsWithIds));
    }
    private ActivityMainBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
        super(bindingComponent, root, 0
            );
        this.mboundView0 = (androidx.constraintlayout.widget.ConstraintLayout) bindings[0];
        this.mboundView0.setTag(null);
        setRootTag(root);
        // listeners
        invalidateAll();
    }

    @Override
    public void invalidateAll() {
        synchronized(this) {
                mDirtyFlags = 0x1L;
        }
        requestRebind();
    }

    @Override
    public boolean hasPendingBindings() {
        synchronized(this) {
            if (mDirtyFlags != 0) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean setVariable(int variableId, @Nullable Object variable)  {
        boolean variableSet = true;
            return variableSet;
    }

    @Override
    protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
        switch (localFieldId) {
        }
        return false;
    }

    @Override
    protected void executeBindings() {
        long dirtyFlags = 0;
        synchronized(this) {
            dirtyFlags = mDirtyFlags;
            mDirtyFlags = 0;
        }
        // batch finished
    }
    private  long mDirtyFlags = 0xffffffffffffffffL;
   
}

生成Binding类的名字很特殊,它与XML布局文件的名字有对应关系,具体的联系就是,以XML布局文件为准,去掉下划线,所有单次以大驼峰的形式按顺序拼接,最后再加上Binding。

绑定布局
var binding: ActivityMainBinding ? = null

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
  binding=DataBindingUtil.setContentView(this,R.layout.activity_main)
}

使用DataBindingUtil类的setContentView()方法对Activity进行绑定,其返回值就是工具生成的Binding类

添加data标签
<data>
   <variable
       name="user"
       type="com.arrom.demo.User" />
</data>
给控件赋值
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{user.name}"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

在XML文件中声明好variable属性后,接下来就可以在XML使用它了。使用variable属性时需要使用到布局表达式: @{ }。可以在布局表达式@{ }中获取传入variable对象的值

binding=DataBindingUtil.setContentView(this,R.layout.activity_main)

val bean = User("arrom", 18);
binding?.user = bean;

在UI界面中使用Binding类为每一个variable标签使用set方法传递数据

响应事件

我们还可以使用DataBinding响应用户事件

第一种方式

binding?.clickBtn?.setOnClickListener { 
    Log.d("MainActivity","按钮点击事件")
}

第二种方式

首先创建一个工具类,在类中定义响应事件的方法

class ButtonClickListener {

    fun clickBtn(view: View){
        Log.d("ButtonClickListener","点击事件")
    }
}

然后在布局文件中添加点击事件的代码

<data>
   <variable
       name="user"
       type="com.arrom.demo.User" />
    <variable
        name="clickBtn"
        type="com.arrom.demo.ButtonClickListener" />
</data>

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@{user.name}"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/clickBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="点击按钮"
        android:onClick="@{clickBtn.clickBtn}"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.73" />

</androidx.constraintlayout.widget.ConstraintLayout>

首先在data标签中为ButtonClickListener类声明对象,在Button的onClick属性中传入布局表达式即可。

include标签

为了能够让布局文件得到复用,在编写布局的时候我们经常会使用include标签,相同结构与内容的布局文件就可以在多处使用。但是如果一个布局文件中使用了DataBinding,同时也使用了include标签,那么如何使用include标签引入的布局文件中中的数据呢?

我们需要在同一级页面的include标签中,通过命名控件xmlns:app来引入布局变量User,将数据对象传递给二级页面

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    >

    <data>
        <variable
            name="user"
            type="com.arrom.demo.User" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <include layout="@layout/use_item"
            app:persondata="@{user}"
            />

    </androidx.constraintlayout.widget.ConstraintLayout>

布局表达式中直接传入页面变量user,include标签属性值可以任意取名

use_item.xml

<layout xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
      <variable
          name="user"
          type="com.arrom.demo.User" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="30dp"
            android:layout_marginBottom="30dp"
            android:lineSpacingExtra="20sp"
            android:text="@{user.name}"
            android:textSize="20sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:layout_editor_absoluteX="157dp"
            tools:ignore="MissingConstraints" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
小结

在编译时,dataBinding会内部处理布局文件,重新生成两个布局文件。一个与布局文件同名,一个叫activity_main-layout.xml

image.png

生成后的activity_main.xml就跟我们普通的xml文件一样,用于Android os的渲染。但是DataBinding会在每个控件上都加上Tag,这个Tag就是用来快速查找和定位具体控件的。

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" android:tag="layout/activity_main_0" 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">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:tag="binding_1"    
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/clickBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="点击按钮"
        android:tag="binding_2"               
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.73" />

</androidx.constraintlayout.widget.ConstraintLayout>

activity_main.xml与不用DataBinding前的布局文件差不多,唯一的区别是加了tag标记。 根布局的tag为layout/activity_main_0

activity_main-layout.xml文件,是DataBinding生成的配置文件,它能够通过文件中的配置信息,快速定位哪一个控件,该显示什么信息。

<Layout layout="activity_main" modulePackage="com.arrom.demo" filePath="app/src/main/res/layout/activity_main.xml" directory="layout" isMerge="false" isBindingData="true" rootNodeType="androidx.constraintlayout.widget.ConstraintLayout">
  <Variables declared="true" type="com.arrom.demo.User" name="user">
    <location startLine="6" startOffset="7" endLine="8" endOffset="39"/>
  </Variables>
  <Variables declared="true" type="com.arrom.demo.ButtonClickListener" name="clickBtn">
    <location startLine="9" startOffset="8" endLine="11" endOffset="55"/>
  </Variables>
  <Targets>
    <Target tag="layout/activity_main_0" view="androidx.constraintlayout.widget.ConstraintLayout">
      <Expressions/>
      <location startLine="14" startOffset="4" endLine="41" endOffset="55"/>
    </Target>
    <Target tag="binding_1" view="TextView">
      <Expressions>
        <Expression text="user.name" attribute="android:text">
          <Location startLine="22" startOffset="12" endLine="22" endOffset="38"/>
          <TwoWay>false</TwoWay>
          <ValueLocation startLine="22" startOffset="28" endLine="22" endOffset="36"/>
        </Expression>
      </Expressions>
      <location startLine="19" startOffset="8" endLine="26" endOffset="55"/>
    </Target>
    <Target id="@+id/clickBtn" tag="binding_2" view="TextView">
      <Expressions>
        <Expression text="clickBtn.clickBtn" attribute="android:onClick">
          <Location startLine="33" startOffset="12" endLine="33" endOffset="49"/>
          <TwoWay>false</TwoWay>
          <ValueLocation startLine="33" startOffset="31" endLine="33" endOffset="47"/>
        </Expression>
      </Expressions>
      <location startLine="28" startOffset="8" endLine="39" endOffset="55"/>
    </Target>
  </Targets>
</Layout>

源码分析

setContentView方法
DataBindingUtil.setContentView(this,R.layout.activity_main)

进入DataBindingUtil类中setContentView 方法

public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity,
        int layoutId) {
    return setContentView(activity, layoutId, sDefaultComponent);
}
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);
}

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

childrenAdded = 1 所以进入bind方法

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

sMapper 是DataBinderMapperImpl

private static DataBinderMapper sMapper = new DataBinderMapperImpl();

进入DataBinderMapperImpl 的getDataBinder 方法

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 ActivityMainBindingImpl(component, view);
        }
        throw new IllegalArgumentException("The tag for activity_main is invalid. Received: " + tag);
      }
    }
  }
  return null;
}

根布局为layout/activity_main_0这个tag所以进入ActivityMainBindingImpl 类中

public ActivityMainBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
    this(bindingComponent, root, mapBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds));
}
private ActivityMainBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
    super(bindingComponent, root, 0
        , (android.widget.TextView) bindings[2]
        );
    this.clickBtn.setTag(null);
    this.mboundView0 = (androidx.constraintlayout.widget.ConstraintLayout) bindings[0];
    this.mboundView0.setTag(null);
    this.mboundView1 = (android.widget.TextView) bindings[1];
    this.mboundView1.setTag(null);
    setRootTag(root);
    // listeners
    invalidateAll();
}

先执行了父类的构造方法,这个父类最后定位到ViewDataBinding这个类

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) {
                final ViewDataBinding binding = getBinding(v);
                binding.mRebindRunnable.run();
                v.removeOnAttachStateChangeListener(this);
            }

            @Override
            public void onViewDetachedFromWindow(View v) {
            }
        };
    }
}

静态代码块中主要是一个监听,简单来说就是,当View和屏幕绑定时,这个监听就会触发,这里指的是mRootView

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

当静态代码块中的监听执行时,这个类的run方法就执行了

protected ViewDataBinding(DataBindingComponent bindingComponent, View root, int localFieldCount) {
    mBindingComponent = bindingComponent;
    mLocalFieldObservers = new WeakListener[localFieldCount];
    this.mRoot = root;
    if (Looper.myLooper() == null) {
        throw new IllegalStateException("DataBinding must be created in view's UI Thread");
    }
    if (USE_CHOREOGRAPHER) {
        mChoreographer = Choreographer.getInstance();
        mFrameCallback = new Choreographer.FrameCallback() {
            @Override
            public void doFrame(long frameTimeNanos) {
                mRebindRunnable.run();
            }
        };
    } else {
        mFrameCallback = null;
        mUIThreadHandler = new Handler(Looper.myLooper());
    }
}

这里使用了Choreographer这个类

回到ActivityMainBindingImpl的构造方法中,查看invalidateAll方法

public void invalidateAll() {
    synchronized(this) {
            mDirtyFlags = 0x4L;
    }
    requestRebind();
}

requestRebind方法是父类ViewDataBinding的方法

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

我们可以看到上面说的mChoreographer,它执行了postFrameCallback方法,当doFrame方法执行时,mRebindRunnable的run方法就执行了。mRebindRunnable中的run方法执行了两个内容:如果mRoot已经和屏幕进行了绑定,那么直接执行executePendingBindings方法;否则的话,添加监听,什么时候绑定了,什么时候执行

进入executePendingBindings方法

public void executePendingBindings() {
    if (mContainingBinding == null) {
        executeBindingsInternal();
    } else {
        mContainingBinding.executePendingBindings();
    }
}

进入executeBindingsInternal 方法中

private void executeBindingsInternal() {
    if (mIsExecutingPendingBindings) {
        requestRebind();
        return;
    }
    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;
}

进入executeBindings方法中(这个方法是一个抽象方法,具体的实现在ActivityMainBindingImpl中)

protected void executeBindings() {
    long dirtyFlags = 0;
    synchronized(this) {
        dirtyFlags = mDirtyFlags;
        mDirtyFlags = 0;
    }
    java.lang.String userName = null;
    com.arrom.demo.User user = mUser;
    com.arrom.demo.ButtonClickListener ClickBtn1 = mClickBtn;
    android.view.View.OnClickListener clickBtnClickBtnAndroidViewViewOnClickListener = null;

    if ((dirtyFlags & 0x5L) != 0) {



            if (user != null) {
                // read user.name
                userName = user.getName();
            }
    }
    if ((dirtyFlags & 0x6L) != 0) {



            if (ClickBtn1 != null) {
                // read clickBtn::clickBtn
                clickBtnClickBtnAndroidViewViewOnClickListener = (((mClickBtnClickBtnAndroidViewViewOnClickListener == null) ? (mClickBtnClickBtnAndroidViewViewOnClickListener = new OnClickListenerImpl()) : mClickBtnClickBtnAndroidViewViewOnClickListener).setValue(ClickBtn1));
            }
    }
    // batch finished
    if ((dirtyFlags & 0x6L) != 0) {
        // api target 1

        this.clickBtn.setOnClickListener(clickBtnClickBtnAndroidViewViewOnClickListener);
    }
    if ((dirtyFlags & 0x5L) != 0) {
        // api target 1

        androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView1, userName);
    }
}

在这里把控件和model进行了绑定,不过此时的mUser还是空。

androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView1, userName);

把从JavaBean属性中读出来的值,设置到View上。也就是实现了Model到View更新。

大致流程图

image.png

setUser方法分析
public void setUser(@Nullable com.arrom.demo.User User) {
    //更新注册
    updateRegistration(0, User);
    this.mUser = User;
    synchronized(this) {
        mDirtyFlags |= 0x1L;
    }
    notifyPropertyChanged(BR.user);
    super.requestRebind();
}

进入ViewDataBinding类中的updateRegistration方法

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,
            ReferenceQueue<ViewDataBinding> referenceQueue
    ) {  
       // 返回一个属性监听器
        return new WeakPropertyListener(viewDataBinding, localFieldId, referenceQueue)
                .getListener();
    }
};

进入updateRegistration方法

protected boolean updateRegistration(int localFieldId, Object observable,
        CreateWeakListener listenerCreator) {
    if (observable == null) {
        return unregisterFrom(localFieldId);
    }
    //将WeakListener 变量保存在mLocalFieldObservers 因为WeakListener是弱引用,所以需要判空,如果是空的就注册
    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;
}

localFieldId 就是BR文件中的id observable就是观察者对象

进入registerTo方法

protected void registerTo(int localFieldId, Object observable,
        CreateWeakListener listenerCreator) {
    if (observable == null) {
        return;
    }
    WeakListener listener = mLocalFieldObservers[localFieldId];
    if (listener == null) {
    //调用CreateWeakListener接口 的create,不能的databinding变量,实现了不同的CreateWeakListener接口
        listener = listenerCreator.create(this, localFieldId, sReferenceQueue);
      
      //这里把新创建的WeakListener 保存在数组,方便下次使用
        mLocalFieldObservers[localFieldId] = listener;
        if (mLifecycleOwner != null) {
            listener.setLifecycleOwner(mLifecycleOwner);
        }
    }
    //listener 是WeakListener 类型, 
    // observable 是业务类型的数据,也就是一种databinding变量
    listener.setTarget(observable);
}

public void setTarget(T object) {
    unregister();
    mTarget = object;
    if (mTarget != null) {
       // mObservable 就是WeakPropertyListener
        mObservable.addListener(mTarget);
    }
}

通过WeakListener监听器中的ObservableReference对象保存观察者与被观察者,当被观察者发生改变的时候,就会找到对应的WeakListener监听器,然后通知观察者做修改

ObservableReference 对应对应的实现类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 WeakListener<Observable> getListener() {
            return mListener;
        }

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

        @Override
        public void removeListener(Observable target) {
            target.removeOnPropertyChangedCallback(this);
        }


       //数据更新后,回调到这里去更新UI
        @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);
        }
    }

target.addOnPropertyChangedCallback(this);调用了BaseObservable 的addOnPropertyChangedCallback函数

@Override
public void addOnPropertyChangedCallback(@NonNull OnPropertyChangedCallback callback) {
    synchronized (this) {
        if (mCallbacks == null) {
            mCallbacks = new PropertyChangeRegistry();
        }
    }
    // 相当于把一个观察者 callback,与被观察者BaseObservable ,建立联系,当BaseObservable 的数据发生变化,通知到callback,实现相应的UI更新
    mCallbacks.add(callback);
}

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

发现和setContentView里面的差不多,最后执行到了mRebindRunnable中的run方法,最后定位到executeBindings方法。

大致流程

image.png

其他知识点

  • @Bindable注解 主要是生成BR中的索引
  • BR文件存储了绑VM的id,功能与R文件类似。
  • DataBinderMapper主要提供了从布局文件layoutId到ViewBinding类的映射。
  • BaseObservable 主要做的就是把OnPropertyChangedCallback添加到PropertyChangeRegistry类型的mCallbacks中
  • PropertyChangeRegistry用于管理Observable回调的实用程序类,继承自CallbackRegistry