DataBinding原理分析

2020-03-30  本文已影响0人  echoSuny

DataBinding即数据绑定,是 Google 在 Jetpack 中推出的一款数据绑定的支持库,利用该库可以实现在页面组件中直接绑定应用程序的数据源。使其维护起来更加方便,架构更明确简介。
1 简单使用
开启DataBinding
在app的build.gradle中添加如下代码:

android {
        .....
        dataBinding{
            enabled true
        }
    }

User.class

public class User extends BaseObservable {

    private String name;

    @Bindable
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        notifyPropertyChanged(BR.name);
    }
}

activity_main.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="uesr"
            type="com.example.binding.User" />
    </data>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">

        <TextView
            android:id="@+id/text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{uesr.name}" />
    </LinearLayout>
</layout>

MainActivity.class

ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        User user = new User();
        test.setName("echo");
        binding.setUesr(user);

2 原理分析
2.1拆分布局文件
首先编译器会首先把布局文件进行拆分,生成的文件目录路径为/app/build/intermediates/data_binding_layout_info_type_merge/debug/mergeDebugResources/out/activity_main-layout.xml
第一部分:

<?xml version="1.0" encoding="utf-8"?>

<Layout layout="activity_main" modulePackage="com.mzw.myrouter" filePath="/Users/echo/demo/Binding/app/src/main/res/layout/activity_main.xml" directory="layout" isMerge="false">
  <!--先前在activity_main.xml中声明的对象user-->
  <Variables declared="true" type="com.example.binding.User" name="uesr">
    <location startLine="6" startOffset="8" endLine="8" endOffset="42"/>
  </Variables>
  <Targets>
   <!-- textview 线形布局 并自动加上了tag = layout/activity_main_0-->
    <Target tag="layout/activity_main_0" view="LinearLayout">
      <Expressions/>
      <location startLine="12" startOffset="4" endLine="24" endOffset="18"/>
    </Target>
      <!-- textview 并自动加上了tag = binding_1-->
    <Target id="@+id/text" tag="binding_1" view="TextView">
      <Expressions>
        // android:text = "@{user.name}"
        <Expression text="uesr.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="18" startOffset="8" endLine="22" endOffset="41"/>
    </Target>
  </Targets>
</Layout>

第二部分的路径是app/build/intermediates/incremental/mergeDebugResources/stripped.dir/layout/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
                                                   <!--对应上面自动生成的tag-->
        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:id="@+id/text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            <!--对应上面自动生成的tag-->
            android:tag="binding_1"     />

    </LinearLayout>

可以看到第二部分的activity_main.xml对比在项目中自己的activity_main.xml 少了data标签下面的内容。也就是说拆分成了data和view两部分。当在view部分解析到tag=binding_1的时候,就可以去data下面通过这个tag找到<Target id="@+id/text" tag="binding_1" view="TextView">和<Expression text="uesr.name" attribute="android:text">,就可以知道TextView的"android:text"需要的数据是uesr.name,这样在执行的时候就可以去user类当中找到user.name属性。
2.2 引用关系
/app/build/generated/data_binding_base_class_source_out/debug/dataBindingGenBaseClassesDebug/out/com/example/binding/databinding目录下,存放着ActivityMainBinding.java。

public abstract class ActivityMainBinding extends ViewDataBinding {
  @NonNull
  public final TextView text;

  @Bindable
  protected User mUesr;

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

  public abstract void setUesr(@Nullable User uesr);
  ......
}

可以看到是一个抽象类,并且可以看到我们调用的setUser()方法,那么具体的逻辑就一定在子类当中。子类的存放的路径为:
app/build/generated/ap_generated_sources/debug/out/com/example/binding。


代码包结构

先前在User类中我们在setName()方法中调用了notifyPropertyChanged(BR.name),那么打开BR.java。

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

  public static final int name = 1;

  public static final int uesr = 2;
}

可以看到编译器自动生成了这些文件,有点类似于R文件,就是用来记录所有的data信息的。下面来看ActivityMainBindingImpl.java的部分代码

public class ActivityMainBindingImpl extends ActivityMainBinding  {

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

    @Override
    public boolean setVariable(int variableId, @Nullable Object variable)  {
        boolean variableSet = true;
        if (BR.uesr == variableId) {
            setUesr((com.example.binding.User) variable);
        }
        else {
            variableSet = false;
        }
            return variableSet;
    }

    public void setUesr(@Nullable com.example.binding.User Uesr) {
        // 更新注册信息
        updateRegistration(0, Uesr);
        // 保存user
        this.mUesr = Uesr;
        synchronized(this) {
            mDirtyFlags |= 0x1L;
        }
        notifyPropertyChanged(BR.uesr);
        super.requestRebind();
    }
}

可以看到setUser()就是之前在MainActivity中调用的方法。由于保存user对象,那么ActivityMainBindingImpl就持有了User的引用。接下来看一下updateRegistration(0, Uesr)都干了什么。

ViewDataBinding.java

// 静态常量 负责创建WeakPropertyListener
private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
        @Override
        public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {         
            return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();
        }
    };

// ViewDataBinding 的内部类 对WeakListener<Observable>进行了一层包装
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);
        }

protected boolean updateRegistration(int localFieldId, Observable observable) {
        return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
    }

private boolean updateRegistration(int localFieldId, Object observable,
            CreateWeakListener listenerCreator) {
        // observable为null,也就是传入的user对象为null 就反注册listener
        if (observable == null) {
            return unregisterFrom(localFieldId);
        }
        // 反之则中数组中根据下标去取listener
        // 下标就是BR文件中声明的int常量
        WeakListener listener = mLocalFieldObservers[localFieldId];
        // 不存在 就创建一个存入数组当中 并把observable传给listener
        if (listener == null) {
            registerTo(localFieldId, observable, listenerCreator);
            return true;
        }
        // 如果数组中之前就存在对应的listener 且对象一样 则do nothing
        if (listener.getTarget() == observable) {
            return false;//nothing to do, same object
        }
       // 如果数组中之前就存在对应的listener 且对象不一样 则反注册
      // 并为新对象重新创建一个listener存入数组
        unregisterFrom(localFieldId);
        registerTo(localFieldId, observable, listenerCreator);
        return true;
    }
引用关系

通过ActivityMainBindingbinding = DataBindingUtil.setContentView(this, R.layout.activity_main)这一行代码Activity和ActivityMainBindingbinding就相互持有对方的引用又因为ActivityMainBindingbinding继承自ViewDataBinding。所以Activity和ViewDataBinding也相互持有对方的引用。WeakListener和ViewDataBinding通过前面提到的数组的关系,也存在互相引用关系。那WeakListener和WeakPropertyListener同样也存在相互引用关系。ViewDataBinding在之前this.mUser = user时也持有了User的引用。最后User也间接的持有了WeakPropertyListener的引用。
引用关系理明白了,那么当User中的name变化的时候,就会是User-> WeakPropertyListener-> WeakListener-> ViewDataBinding-> Activity,最终TextView上的文字就更新了。
2.3 流程分析
从DataBindingUtil.setContentView()入手

public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity,
            int layoutId) {
        return setContentView(activity, layoutId, sDefaultComponent);
    }
//省略中间调用的部分代码
static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View[] roots,
            int layoutId) {
        return (T) sMapper.getDataBinder(bindingComponent, roots, layoutId);
    }

可以看到最后是调用了DataBindingUtil内部的静态方法bind()。sMapper是DataBinderMapper,这是一个抽象类,getDataBinder()是一个抽象方法,下面是具体的实现:

DataBinderMapperImpl.java  // 就是上方代码包结构图片中中最下面的那个类
@Override
  public ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) {
    int localizedLayoutId = INTERNAL_LAYOUT_ID_LOOKUP.get(layoutId);
    if(localizedLayoutId > 0) {
     // 得到view上的tag
      final Object tag = view.getTag();
      if(tag == null) {
        throw new RuntimeException("view must have a tag");
      }
      switch(localizedLayoutId) {
        case  LAYOUT_ACTIVITYMAIN: {
          // 这里就与上面的activity_main.xml中的自动给LinearLayout添加的tag对应上了
          if ("layout/activity_main_0".equals(tag)) {
            // 生成对应的ViewBinding
            return new ActivityMainBindingImpl(component, view);
          }
          throw new IllegalArgumentException("The tag for activity_main is invalid. Received: " + tag);
        }
      }
    }
    return null;
  }

下面看ActivityMainBindingImpl中都做了什么:

public ActivityMainBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
        //内部调用了下面三个参数的构造方法 
        this(bindingComponent, root, mapBindings(bindingComponent, root, 2, sIncludes, sViewsWithIds));
    }

    private ActivityMainBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
        super(bindingComponent, root, 1
            , (android.widget.TextView) bindings[1]
            );
        this.mboundView0 = (android.widget.LinearLayout) bindings[0];
        this.mboundView0.setTag(null);
        // this.text这里就解释了为什么Databinding可以通过binding点上view的id就可以找到控件了
        this.text.setTag(null);
        setRootTag(root);
        // listeners
        invalidateAll();
    }

接着看一下其中mapBindings()方法都干了什么:

protected static Object[] mapBindings(DataBindingComponent bindingComponent, View[] roots, int numBindings, ViewDataBinding.IncludedLayouts includes, SparseIntArray viewsWithIds) {
       // 初始化一个数组 长度为numBindings
        Object[] bindings = new Object[numBindings];
        for(int i = 0; i < roots.length; ++i) {
            mapBindings(bindingComponent, roots[i], bindings, includes, viewsWithIds, true);
        }

        return bindings;
    }

    private static void mapBindings(DataBindingComponent bindingComponent, View view, Object[] bindings, ViewDataBinding.IncludedLayouts includes, SparseIntArray viewsWithIds, boolean isRoot) {
        // return v != null ? (ViewDataBinding)v.getTag(id.dataBinding) : null;
        // 上面是getBinding()所做的事情,就是根据tag去取view对应的binding
        ViewDataBinding existingBinding = getBinding(view);
        // 如果之前没有生成binding才会去做下面的解析
        if (existingBinding == null) {
            Object objTag = view.getTag();
            String tag = objTag instanceof String ? (String)objTag : null;
            boolean isBound = false;
            int indexInIncludes;
            int id;
            int count;
            //是根布局并且以tag是以layout开头 也就是LinearLayout的tag="layout/activity_main_0"
            if (isRoot && tag != null && tag.startsWith("layout")) {
                id = tag.lastIndexOf(95);
                if (id > 0 && isNumeric(tag, id + 1)) {
                    count = parseTagInt(tag, id + 1);
                    if (bindings[count] == null) {
                        // 把LinearLayout存入数组
                        bindings[count] = view;
                    }

                    indexInIncludes = includes == null ? -1 : count;
                    isBound = true;
                } else {
                    indexInIncludes = -1;
                }
              // tag是以binding_开头 也就是TextView的tag="binding_1"
            } else if (tag != null && tag.startsWith("binding_")) {
                id = parseTagInt(tag, BINDING_NUMBER_START);
                if (bindings[id] == null) {
                    // TextView 存入数组
                    bindings[id] = view;
                }

                isBound = true;
                indexInIncludes = includes == null ? -1 : id;
            } else {
                indexInIncludes = -1;
            }
                  ...... // 省略部分代码
                  // 递归调用 例如ViewGroup嵌套ViewGroup 就会递归调用直到遍历完所有的view
                    if (!isInclude) {
                        mapBindings(bindingComponent, child, bindings, includes, viewsWithIds, false);
                    }
                }
            }
        }
    }

可以看到最终就是把所有的view解析出来并存入到数组当中。到此XML文件中的内容就被全部解析出来了。
2.4 数据绑定
拿到了所有的view,也拿到了数据User类,下面就来看一下是如何把数据绑定到view上的。

ViewDataBinding.java
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) {
                }
            };
        }

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();
        }
    };
    // 最终会调到这个抽象方法
    // 那么我们就需要去ActivityMainBindingImpl.java查看具体实现
   protected abstract void executeBindings();
ActivityMainBindingImpl.java
@Override
    protected void executeBindings() {
        long dirtyFlags = 0;
        synchronized(this) {
            dirtyFlags = mDirtyFlags;
            mDirtyFlags = 0;
        }
        // 取出之前保存的user对象
        com.example.binding.User uesr = mUesr;
        // 声明一个空的String
        java.lang.String uesrName = null;
        // 如果有多个view则会有多个这样的if
        if ((dirtyFlags & 0x7L) != 0) {
                if (uesr != null) {
                    //从user中取出name的值并赋值
                    uesrName = uesr.getName();
                }
        }
        // batch finished
        if ((dirtyFlags & 0x7L) != 0) {
            //调用BindingAdapter的setText
            androidx.databinding.adapters.TextViewBindingAdapter.setText(this.text, uesrName);
        }
    }


TextViewBindingAdapter.java
   @BindingAdapter("android:text")
    public static void setText(TextView view, CharSequence text) {
        final CharSequence oldText = view.getText();
        if (text == oldText || (text == null && oldText.length() == 0)) {
            return;
        }
        if (text instanceof Spanned) {
            if (text.equals(oldText)) {
                return; // No change in the spans, so don't set anything.
            }
        } else if (!haveContentsChanged(text, oldText)) {
            return; // No content changes, so don't set anything.
        }
        // 设置到view上
        view.setText(text);
    }

3 真正的推手
前面看到做出绑定动作都是在一个runnable当中,那么谁才是真正执行着个任务的人呢?不知道是否还记得在User类当中的setName()方法中的notifyPropertyChanged(BR.name)。就是这一行代码使整个工作得以推进。

BaseObservable.java
public void notifyPropertyChanged(int fieldId) {
        synchronized (this) {
            if (mCallbacks == null) {
                return;
            }
        }
      // PropertyChangeRegistry mCallbacks 是BaseObservable的成员变量
        mCallbacks.notifyCallbacks(this, fieldId, null);
    }
    

CallbackRegistry.java
    public synchronized void notifyCallbacks(T sender, int arg, A arg2) {}
    ...... // 省略一些回调步骤 最终会回调到下面的方法
    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 是CallbackRegistry的内部抽象类NotifierCallback
                mNotifier.onNotifyCallback(mCallbacks.get(i), sender, arg, arg2);
            }
            bitMask <<= 1;
        }
    }


PropertyChangeRegistry.java
     private static final NotifierCallback<OnPropertyChangedCallback, Observable, Void> NOTIFIER_CALLBACK = new NotifierCallback<OnPropertyChangedCallback, Observable, Void>() {
        public void onNotifyCallback(OnPropertyChangedCallback callback, Observable sender, int arg, Void notUsed) {
           // 回调到这里
            callback.onPropertyChanged(sender, arg);
        }
    };

PropertyChangeRegistry.java是不是很眼熟?对,这就是上面BaseObservable.java的成员属性,绕了一圈又回来了。别急接下来还得绕。

public interface Observable {

    void addOnPropertyChangedCallback(OnPropertyChangedCallback callback);

    void removeOnPropertyChangedCallback(OnPropertyChangedCallback callback);

    abstract class OnPropertyChangedCallback {
         // 回调到此处
        public abstract void onPropertyChanged(Observable sender, int propertyId);
    }
}

点进去PropertyChangeRegistry.java的onPropertyChanged)最后又回到了BaseObservable.java的父类Observable的内部类当中了。这个内部类其中的一个实现类就是WeakPropertyListener

private static class WeakPropertyListener extends Observable.OnPropertyChangedCallback
            implements ObservableReference<Observable> {
      
        ...... //省略部分代码
        @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?
            }
            // 调用了ViewDataBinding的方法
            binder.handleFieldChange(mListener.mLocalFieldId, sender, propertyId);
        }
    }
  

ViewDataBinding.java
private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
        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;
        }
        // 这是一个抽象方法 具体实现在ActivityMainBindingImpl当中
        // 判断对象或值是否发生了改变
        boolean result = onFieldChange(mLocalFieldId, object, fieldId);
        // 如果发生了变化
        if (result) {
            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 {
                // 调用主线程的handler发送任务
                mUIThreadHandler.post(mRebindRunnable);
            }
        }
    }

可以看到经过层层回调,最终是发了一个handler来推动2.4部分的数据绑定的整个流程。

上一篇下一篇

猜你喜欢

热点阅读