美文网首页
DataBinding原理分析

DataBinding原理分析

作者: echoSuny | 来源:发表于2020-03-30 22:16 被阅读0次

    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部分的数据绑定的整个流程。

    相关文章

      网友评论

          本文标题:DataBinding原理分析

          本文链接:https://www.haomeiwen.com/subject/hxkvuhtx.html