再探DataBinding

其实说到DataBinding, 不管你使用MVP,MVVM,估计都有用到,数据和View的双向绑定是很爽的,相信很多小伙伴是用的很6,但是原理以及流程不太清楚。今天分享一起学习下DataBinding的相关流程。

举个例子

public class User extends BaseObservable {
    private String name;

    private String age;

    public User(String name, String age) {
        this.name = name;
        this.age = age;
    }

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

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

    @Bindable
    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
        notifyPropertyChanged(BR.age);
    }
}

然后修改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="user"
            type="com.example.jetpackproject.databinding.User" />
    </data>

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".databinding.DatabindingActivity">

        <TextView
            android:id="@+id/tv1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{user.name}"/>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{user.age}"/>
    </LinearLayout>
</layout>

最后将两者绑定起来:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        viewDataBinding = DataBindingUtil.setContentView(this, R.layout.activity_databinding);
        user = new User("hello", 10 + "");
        viewDataBinding.setUser(user);
        new Thread() {
            @Override
            public void run() {
                super.run();
                for (int i = 0; i < 10; i++) {
                    user.setName(user.getName() + "");
                    //方法2:
//                    viewDataBinding.setVariable(BR.user, user);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            }
        }.start();
    }

上述示例是一个简单的DataBinding的使用,需要注意一点:

通过调用ViewDataBinding的setVariable方法和notifyPropertyChanged效果是一样的,都可以实现数据修改View接着修改的效果。

现在有几个问题,需要考虑下:

  1. View和Data是怎样绑定的?
  2. Data修改,View是怎样修改的?
  3. setVariable和notifyPropertyChanged有什么关系?
  4. DataBinding是怎样避免使用findViewById的?

带着这些问题,开始源码分析流程。 在开始分析之前,看三个东西:

APT自动生成的代码:

当在XML中写完代码之后,编译一下,会自动生成两个XML文件:

上图的路径是:项目跟路径 ——》app ——》build ——》intermediates 。

注意:这里自动生成的XML文件名,是和自己在项目中使用DataBinding的XML的文件名称关联的。

其中第一个XML文件内容格式化之后,就是下面的东西:

在XML文件中,将每个标签都设置了一个tag, 然后这个tag对应这一个View,这个tag和另一个xml文件中的View是一一对应的,通过这个tag可以获取到View对应的相关信息:
换句话说就是,通过上一个XML文件的Tag,对应的去找第二个XML的View相关信息。

自己使用了DataBinding的XML文件生成了两个XML文件,这两个文件的关联关系如上图所示。

根据XML以及注解,同样生成了相应的Java代码:
在这里插入图片描述
DataBindingMapperImpl,BR, 以及XXXBindingImpl 三个文件是自动生成的,XXXBindingImpl对应XML:activity_databinding.xml。其中Activity DatabindingBinding类是ViewDataBinding的实现类。

BR类里面有很多静态常量,这些常量都是添加了Bindable注解的方法:

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

知道了这些自动生成的代码,开始带着上述的那四个问题分析流程。

入口setContentView方法

在Activity的onCreate方法内,将XML扔给了DataBindingUtil,并且传入了Activity本身:

    public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity,
            int layoutId, @Nullable DataBindingComponent bindingComponent) {
          // 内部还是调用了Activity的setContentView
        activity.setContentView(layoutId);
        ......
        //
        return bindToAddedViews(bindingComponent, contentView, 0, layoutId);
    }

其实在DataBindingUtil内部还是调用了Activity的setContentView方法,然后又拿着R.id.content的View给了bindToAddedViews作为参数,最终会进入:bind

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

这里的sMapper就是抽象类DataBinderMapper,具体实现类是:DataBinderMapperImpl类,因此执行的是实现类DataBinderMapperImpl的getDataBinder 方法:

 @Override
  public ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) {
    .....
    return new ActivityDatabindingBindingImpl(component, view);
    ......
  }

创建了ActivityDatabindingBindingImpl类实例, 然后我们进入构造方法看看初始化了什么东西。

    public ActivityDatabindingBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
    //通过mapBindings方法获取到XML中所有的View,并且存储到一个数组中
        this(bindingComponent, root, mapBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds));
    }
    private ActivityDatabindingBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
        super(bindingComponent, root, 1
            , (android.widget.TextView) bindings[1]
            );
         // 给成员变量View赋值
        this.mboundView0 = (android.widget.LinearLayout) bindings[0];
        this.mboundView0.setTag(null);
        this.mboundView2 = (android.widget.TextView) bindings[2];
        this.mboundView2.setTag(null);
        //将标记tag置为null
        this.tv1.setTag(null);
        setRootTag(root);
        // listeners
        invalidateAll();
    }

在构造方法中,首先通过调用mapBindings方法获取到所有的子View对象。那么他是怎么获取子View对象的呢?

  private static void mapBindings(DataBindingComponent bindingComponent, View view,
            Object[] bindings, IncludedLayouts includes, SparseIntArray viewsWithIds,
            boolean isRoot) {
        final int indexInIncludes;
        final ViewDataBinding existingBinding = getBinding(view);
        if (existingBinding != null) {
            return;
        }
        Object objTag = view.getTag();
        final String tag = (objTag instanceof String) ? (String) objTag : null;
        boolean isBound = false;
        if (isRoot && tag != null && tag.startsWith("layout")) { // XML中的tag是否是layout开头的
            final int underscoreIndex = tag.lastIndexOf('_');
            if (underscoreIndex > 0 && isNumeric(tag, underscoreIndex + 1)) {
                final int index = parseTagInt(tag, underscoreIndex + 1);
                if (bindings[index] == null) {
                    bindings[index] = view;
                }
                indexInIncludes = includes == null ? -1 : index;
                isBound = true;
            } else {
                indexInIncludes = -1;
            }
        } else if (tag != null && tag.startsWith(BINDING_TAG_PREFIX)) {
            int tagIndex = parseTagInt(tag, BINDING_NUMBER_START);
            if (bindings[tagIndex] == null) {
                bindings[tagIndex] = view;
            }
            isBound = true;
            indexInIncludes = includes == null ? -1 : tagIndex;
        } else {
            // Not a bound view
            indexInIncludes = -1;
        }
        if (!isBound) { // view是否设置了id
            final int id = view.getId();
            if (id > 0) {
                int index;
                if (viewsWithIds != null && (index = viewsWithIds.get(id, -1)) >= 0 &&
                        bindings[index] == null) {
                    bindings[index] = view;
                }
            }
        }

        if (view instanceof  ViewGroup) { //是否是ViewGroup
            final ViewGroup viewGroup = (ViewGroup) view;
            final int count = viewGroup.getChildCount();
            int minInclude = 0;
            for (int i = 0; i < count; i++) { //循环每个子View
                final View child = viewGroup.getChildAt(i);
                boolean isInclude = false;
                if (indexInIncludes >= 0 && child.getTag() instanceof String) {
                    String childTag = (String) child.getTag();
                    if (childTag.endsWith("_0") &&
                            childTag.startsWith("layout") && childTag.indexOf('/') > 0) {
                        // This *could* be an include. Test against the expected includes.
                        int includeIndex = findIncludeIndex(childTag, minInclude,
                                includes, indexInIncludes);
                        if (includeIndex >= 0) {
                            isInclude = true;
                            minInclude = includeIndex + 1;
                            final int index = includes.indexes[indexInIncludes][includeIndex];
                            final int layoutId = includes.layoutIds[indexInIncludes][includeIndex];
                            int lastMatchingIndex = findLastMatching(viewGroup, i);
                            if (lastMatchingIndex == i) {
                            	//包含include标签,重新执行交给DataBindingUtil类处理
                                bindings[index] = DataBindingUtil.bind(bindingComponent, child,
                                        layoutId);
                            } else {
                                final int includeCount =  lastMatchingIndex - i + 1;
                                final View[] included = new View[includeCount];
                                for (int j = 0; j < includeCount; j++) {
                                    included[j] = viewGroup.getChildAt(i + j);
                                }
                                bindings[index] = DataBindingUtil.bind(bindingComponent, included,
                                        layoutId);
                                i += includeCount - 1;
                            }
                        }
                    }
                }
                if (!isInclude) { //是否包含 标签include。不包含的话,就递归获取view
                    mapBindings(bindingComponent, child, bindings, includes, viewsWithIds, false);
                }
            }
        }
    }

上述代码很长,实际上就是通过tag获取对应的View,然后存入到数组bindings中。方法其实就是个递归,如果XML中定义的是ViewGroup类型的,那么就交给DataBindingUtil重新执行之前的操作;如果不包含,那么到最后就递归通过tag继续获取View。

这里的tag,实际上就是之前开头说的那个XML文件,通过tag文件可以对应的获取第二个XML的View的相关信息。

总体来说:这里就做了一件事情,通过tag获取View,并且将View存入到数组中并返回该数组。

返回该数组之后,将数组中的所有子View赋值给成员变量(XML中View有ID的就直接使用ID设置;没有设置ID的就创建成员变量)。

然后执行了invalidateAll方法,该方法可以看作是初始化View上的数据。最终会执行requestRebind方法:

        protected void requestRebind() {
        //这里判断View是否包含include标签的布局
        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 { //  我们分析<16的
                mUIThreadHandler.post(mRebindRunnable);
            }
        }
    }

该方法最后开启了一个线程mRebindRunnable:

    /**
     * Runnable executed on animation heartbeat to rebind the dirty Views.
     */
    private final Runnable mRebindRunnable = new Runnable() {
        @Override
        public void run() {
            .....
            executePendingBindings();
            
        }
    };
        public void executePendingBindings() {
        if (mContainingBinding == null) { // 由于我们的XML没有包含include标签,因此进入此判断
            executeBindingsInternal();
        } else {
            mContainingBinding.executePendingBindings();
        }
    }
    private void executeBindingsInternal() {
        .......
        executeBindings();
        .......
    }

最终进入executeBindings方法,该方法是个抽象方法,其实现是在:ViewDataBinding的实现类ActivityDatabindingBindingImpl。

注意:这里是ActivityDatabindingBindingImpl, 这个自动生成的类是根据你的XML变化的。比如:如果你定义的是abc.xml,那么生成的实现类就是:AbcBindingImpl。

    @Override
    protected void executeBindings() {
        ......
        if ((dirtyFlags & 0xfL) != 0) {
            if ((dirtyFlags & 0xbL) != 0) {
                    if (user != null) {
                        // read user.name
                        userName = user.getName(); // 通过调用注解Bindable的方法,获取值
                    }
            }
            if ((dirtyFlags & 0xdL) != 0) {

                    if (user != null) {
                        // read user.age
                        userAge = user.getAge();
                    }
            }
        }
        // batch finished
        if ((dirtyFlags & 0xdL) != 0) {
            // api target 1 TextView设置值
			androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView2, userAge);
        }
        if ((dirtyFlags & 0xbL) != 0) {
            // api target 1

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

最终调用了TextViewBindingAdapter的setText方法:

    @BindingAdapter("android:text")
    public static void setText(TextView view, CharSequence text) {
        .....
        //给TextView设置value
        view.setText(text);
    }

至此setContentView流程就分析完了,重新捋一遍:
首先创建了XXXBindingImpl类,然后在构造方法中,通过tag获取到了所有的子view,并且添加到了一个数组中,并且将数组中的每一个值都赋值给了成员变量,或者直接通过ID设置了值。 接下来就是给View初始化数据, 通过通过开启一个线程,最后通过XXXBindingAdapter成功初始化了View。

通过上述流程分析,就解决了问题1 和 问题4

当我们有数据变化时,会直接调用:

viewDataBinding.setVariable(BR.user, user);

已setVariable为入口看下内部是怎么实现View的修改的。

setVariable方法

    @Override
    public boolean setVariable(int variableId, @Nullable Object variable)  {
        boolean variableSet = true;
        if (BR.user == variableId) { // 如果 BR.user == 传入的ID
        // 调用了setUser方法
            setUser((com.example.jetpackproject.databinding.User) variable);
        }
        else { // 否则直接返回
            variableSet = false;
        }
            return variableSet;
    }

通过上述代码我们可以得出两点:

  1. 如果传入的是:BR.user(也就是继承BaseObservable),那么就调用方法;
  2. 如果不是就直接返回,那么也就是说:setVariable(BR.name, “hello”);是无效的!!!

继续深入:

    public void setUser(@Nullable com.example.jetpackproject.databinding.User User) {
    	//更新注册
        updateRegistration(0, User);
        this.mUser = User;
        synchronized(this) {
            mDirtyFlags |= 0x1L;
        }
        //这里调用了notifyPropertyChanged方法,(先不要管参数)
        notifyPropertyChanged(BR.user);
        super.requestRebind();
    }

首先调用了updateRegistration方法,这方法就是给每一个BR下的静态常量添加一个监听器:

    protected boolean updateRegistration(int localFieldId, Observable observable) {
        return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
    }
    private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
        @Override
        public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
            return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();
        }
    };
    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;
    }
    protected void registerTo(int localFieldId, Object observable,
            CreateWeakListener listenerCreator) {
      	.....
      	//这里的listener是WeakListener类的实例对象, observable就是User的实例对象
        listener.setTarget(observable);
    }

首先看下数组mLocalFieldObservers, 这个东西存储的就是监听器(WeakListener),BR文件中的数组一次从0开始,也就是说为每一个BR的常量都存储了个监听器,并且下标索引就是BR中常量的值。

最后调用了监听器的setTarget方法:

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

这里的mObservable就是WeakPropertyListener类的实例对象,

        @Override
        public void addListener(Observable target) {
        //target就是我们的User类的实例对象(注意:该方法在父类中)
            target.addOnPropertyChangedCallback(this);
        }

addOnPropertyChangedCallback的实现是在BaseObservable类中:

    @Override
    public void addOnPropertyChangedCallback(@NonNull OnPropertyChangedCallback callback) {
        synchronized (this) {
            if (mCallbacks == null) {
                mCallbacks = new PropertyChangeRegistry();
            }
        }
        mCallbacks.add(callback);
    }

由于callback参数也就是WeakPropertyListener本身,因此也就是说User类间接持有了WeakPropertyListener的引用。

回过头在看下,updateRegistration方法之后调用了notifyPropertyChange方法,如果不管参数的前提下,是不是可以理解为:setVariable内部就是调用了notifyPropertyChanged方法呢? 答案是肯定的。

    public void notifyPropertyChanged(int fieldId) {
        synchronized (this) {
            if (mCallbacks == null) {
                return;
            }
        }
        mCallbacks.notifyCallbacks(this, fieldId, null);
    }

这里的mCallbacks就是PropertyChangeRegistry实例对象,从类BaseObservable的addOnPropertyChangedCallback方法可以看出来:

    public void notifyChange(@NonNull Observable observable, int propertyId) {
        notifyCallbacks(observable, propertyId, null);
    }
    public synchronized void notifyCallbacks(T sender, int arg, A arg2) {
      ......
      notifyRecurse(sender, arg, arg2);
      .......
    }
    private void notifyRecurse(T sender, int arg, A arg2) {
        ........
        notifyCallbacks(sender, arg, arg2, startCallbackIndex, callbackCount, 0);
    }
    private void notifyCallbacks(T sender, int arg, A arg2, final int startIndex,
            final int endIndex, final long bits) {
        ....
        mNotifier.onNotifyCallback(mCallbacks.get(i), sender, arg, arg2);
        .....
    }

通过层层调用最终,进入到了notifyCallbacks方法,这里mNotifier就是PropertyChangeRegistry类中的NOTIFIER_CALLBACK常量。
而mCallbacks是一个数组,其内部存储的就是:WeakPropertyListener。

这里mCallbacks存储的数据类型,可以在ViewDataBinding类中找到。在上文分析的类BaseObservable的addOnPropertyChangedCallback方法中。

因此就进入到了WeakPropertyListener的onPropertyChanged方法中:

        @Override
        public void onPropertyChanged(Observable sender, int propertyId) {
            ViewDataBinding binder = mListener.getBinder();
            ......
            binder.handleFieldChange(mListener.mLocalFieldId, sender, propertyId);
        }
   
       private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
        ......
        //修改二进制的标记值
        boolean result = onFieldChange(mLocalFieldId, object, fieldId);
        if (result) {// 是否修改了值
        	//最后赋值
            requestRebind();
        }
    }

看到requestRebind方法,就和setContentView流程分析的代码重合了。

实际上总体流程就是Activity,ViewDataBinding, WeakListener, WeakPropertyListener和ViewModel(也就是User), 这四个类是是互相关联的,ViewModel修改了值,是通过WeakPropertyListener ——》 WeakListener——》ViewDataBinding——》Activity 的:
在这里插入图片描述

最后附上一张流程图, 以setVariable为例:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值