Jetpack之DataBinding源码分析

283 阅读4分钟

一、最官方的使用方法,不多BB直接上链接

DataBinding使用简介: 戳我👉

二、源码分析

1、我们先说一下最简单使用

  1. 先看一下我们databinding的xml
<?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>
        <variable
            name="testViewModel"
            type="com.sun.kotlindemo.dataBinding.TestViewModel" />
    </data>

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

        <TextView
            android:id="@+id/name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{testViewModel.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/description"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{testViewModel.desc}"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/name" />

        <EditText
            android:id="@+id/editText"
            android:layout_width="300dp"
            android:layout_height="100dp"
            android:background="#00acff"
            android:gravity="center"
            android:text="@={testViewModel.name}"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
        <TextView
            android:id="@+id/textView"
            android:layout_width="300dp"
            android:layout_height="100dp"
            android:background="#00acff"
            android:layout_marginTop="10dp"
            android:text="点我修改名字"
            android:gravity="center"
            app:layout_constraintTop_toBottomOf="@+id/editText"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"/>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
  1. 我们来看下activity中的使用

image.png

  1. 我们来看下TestViewModel

image.png

以上就是DataBinding的基本使用,当我们点击@+id/textView时,@+id/name 和 @+id/editText都会随之改变,当我们在@+id/editText 中输入文字时,@+id/name也会更新为editView输入的文字,这就是DataBinding+ViewModel+LiveData实现的双向绑定

2、我们先说一下最简单使用

  1. 先看xml的结构
<?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
        <variable
            name="testViewModel"
            type="com.sun.kotlindemo.dataBinding.TestViewModel" />
    </data>

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

        //布局文件

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

来看一下经过编译后生成的文件

image.png 这个文件就是我们的xml生成的用于DataBinding使用的xml文件,在这里对我们的view进行了包装,对每一view都添加了一个tag,并且生成一个属性指向我们viewModel中的data

image.png

  1. 接下来我们看activity中的DataBindingUtil.setContentView()做了什么
DataBindingUtil.setContentView<ActivityDataBindingTextViewModelBinding>(this, R.layout.activity_data_binding_text_view_model)

点进去

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

继续点setContentView()

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);
    //将我们的xml绑定在根布局上
    return bindToAddedViews(bindingComponent, contentView, 0, layoutId);
}

继续点bindToAddedViews()

private static <T extends ViewDataBinding> T bindToAddedViews(DataBindingComponent component,
        ViewGroup parent, int startChildren, int layoutId) {
        //获取根布局中的子view个数,也就是我们的xml中view的个数
    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);
    }
}

继续点bind()

//绑定多个view
static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View[] roots,
        int layoutId) {
    return (T) sMapper.getDataBinder(bindingComponent, roots, layoutId);
}
//绑定单个view
static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View root,
        int layoutId) {
    return (T) sMapper.getDataBinder(bindingComponent, root, layoutId);
}

继续点sMapper.getDataBinder(),在DataBinderMapper中.getDataBinder()是抽象的,看我们生成的类【这里强调一下,这个类是生成的类】DataBinderMapperImpl中这个方法

image.png

@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_ACTIVITYDATABINDINGBEAN: {
        if ("layout/activity_data_binding_bean_0".equals(tag)) {
          return new ActivityDataBindingBeanBindingImpl(component, view);
        }
        throw new IllegalArgumentException("The tag for activity_data_binding_bean is invalid. Received: " + tag);
      }
      case  LAYOUT_ACTIVITYDATABINDINGTEXTVIEWMODEL: {
          //这个就是我们写的xml中的ConstraintLayout生成的
        if ("layout/activity_data_binding_text_view_model_0".equals(tag)) {
            //继续看这个
          return new ActivityDataBindingTextViewModelBindingImpl(component, view);
        }
        throw new IllegalArgumentException("The tag for activity_data_binding_text_view_model is invalid. Received: " + tag);
      }
    }
  }

继续点 ActivityDataBindingTextViewModelBindingImpl()

public ActivityDataBindingTextViewModelBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
    //这里这个5就是我们xml中view的数量,
    this(bindingComponent, root, mapBindings(bindingComponent, root, 5, sIncludes, sViewsWithIds));
}

继续点 mapBindings()

protected static Object[] mapBindings(DataBindingComponent bindingComponent, View root,
        int numBindings, IncludedLayouts includes, SparseIntArray viewsWithIds) {
        //创建一个大小为5的数组
    Object[] bindings = new Object[numBindings];
    mapBindings(bindingComponent, root, bindings, includes, viewsWithIds, true);
    return bindings;
}

继续点 mapBindings()

image.png

  1. 接下来我们来看数据的绑定
dataBinding.testViewModel = viewModel

点击textViewModel是跳转不到生成的代码中的,我们自己打开

image.png

打开这个文件我们可以看到

public void setTestViewModel(@Nullable com.sun.kotlindemo.dataBinding.TestViewModel TestViewModel) {
    this.mTestViewModel = TestViewModel;
    synchronized(this) {
        mDirtyFlags |= 0x4L;
    }
    notifyPropertyChanged(BR.testViewModel);
    super.requestRebind();
}

点击 super.requestRebind();

image.png

点击 mRebindRunnable

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

点击executePendingBindings()

public void executePendingBindings() {
    if (mContainingBinding == null) {
        //会走这个方法
        executeBindingsInternal();
    } else {
        mContainingBinding.executePendingBindings();
    }
}

点击executeBindingsInternal()

image.png

点击 executeBindings(),你会发现这个是一个抽象方法,它的实现在我们生成的 ActivityDataBindingTextViewModelBindingImpl 中,进入看这个方法

image.png

点击updateLiveDataRegistration()

protected boolean updateLiveDataRegistration(int localFieldId, LiveData<?> observable) {
    mInLiveDataRegisterObserver = true;
    try {
        //会走这里
        return updateRegistration(localFieldId, observable, CREATE_LIVE_DATA_LISTENER);
    } finally {
        mInLiveDataRegisterObserver = false;
    }
}

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

点击 setTarger()

public void setTarget(T object) {
   unregister();
   mTarget = object;
   if (mTarget != null) {
       //会走这里
       mObservable.addListener(mTarget);
   }
}

点击 addListener(),你会发现这是一个抽象方法,我们看LiveDataListener中的addListener()

image.png

image.png

到这里,将我们的MutableLiveData类型的name进行observe绑定,绑定的观察者就是这个LiveDataListener,它实现类 Observe 接口

private static class LiveDataListener implements Observer,

绑定监听结束,那么只要这个name发生变化,就会执行LiveDataListener的onChanged()

重头戏:数据变化后,更新UI

  1. 当我们点击@+id/textView去改变ViewModel中的name值,也就是数据发生变化,那么它是怎么更新布局的呢,我们来看 LiveDataListener的onChanged()
@Override
public void onChanged(@Nullable Object o) {
    ViewDataBinding binder = mListener.getBinder();
    if (binder != null) {
        binder.handleFieldChange(mListener.mLocalFieldId, mListener.getTarget(), 0);
    }
}

点击 handleFieldChange()

image.png

点击 requestRebind()会把之前的流程走一遍,最终会走到 我们生成的类 ActivityDataBindingTextViewModelBindingImpl 中的 executeBindings()

image.png

重头戏:UI变化后,更新数据 5. 在ActivityDataBindingTextViewModelBindingImpl 中的 executeBindings()中,为editview添加了变化的监听,我们看到最后一个参数是 editTextandroidTextAttrChanged,记住它

androidx.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.editText, (androidx.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.OnTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.AfterTextChanged)null, editTextandroidTextAttrChanged);

点击 setTextWatcher()

image.png

我们先看一下 editTextandroidTextAttrChanged 是什么?它是一个接口的实现

private androidx.databinding.InverseBindingListener editTextandroidTextAttrChanged = new androidx.databinding.InverseBindingListener() {
    @Override
    public void onChange() {
        //获取editText的值
        java.lang.String callbackArg_0 = androidx.databinding.adapters.TextViewBindingAdapter.getTextString(editText);

        androidx.lifecycle.MutableLiveData<java.lang.String> testViewModelName = null;

        com.sun.kotlindemo.dataBinding.TestViewModel testViewModel = mTestViewModel;

        java.lang.String testViewModelNameGetValue = null;
 
        boolean testViewModelJavaLangObjectNull = false;
  
        boolean testViewModelNameJavaLangObjectNull = false;

        testViewModelJavaLangObjectNull = (testViewModel) != (null);
        if (testViewModelJavaLangObjectNull) {

            testViewModelName = testViewModel.getName();

            testViewModelNameJavaLangObjectNull = (testViewModelName) != (null);
            if (testViewModelNameJavaLangObjectNull) 
                //在这里,将EditText的text赋值给了TestViewModel中的name
                testViewModelName.setValue(((java.lang.String) (callbackArg_0)));
            }
        }
    }
};

到这里,已经将editText输入的值,更新给了TestViewModel中的name了,实现了UI变化 ---> 数据更新

三、END~