DataBinding原理分析

前言

现在公司用的框架是MVVM,用到MVVM肯定少不了databinding

最近业务上遇到一个问题,需要动态生成一张图片,并将图片分享出去。我的思路就是写一个xml动态生成一个View,利用databinding绑定这个View,然后获取数据刷新对应的ViewModel,再将View转化成Bitmap分享出去。结果发现,数据并没有填充到View上。这是为什么呢?

当时的猜想,可能是因为我需要的这个View并没有加载到屏幕上而databinding应该是和生命周期有关的,所以databinding不做处理。

这周末正好有空,所以来了解一下databinding。

databinding生成的文件

我们使用databinding,会绑定一个xml,这个过程中,编译器会默默地帮我们生成一系列文件,如下图:
这里写图片描述
当然以上只是部分,还有xml文件等等。

activity_test.xml

位于app/build/intermediates/data-binding-layout-out/debug/layout目录下

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









    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent" android:tag="layout/activity_test_0" xmlns:android="http://schemas.android.com/apk/res/android">

        <TextView
            android:id="@+id/content"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:tag="binding_1"                   />
    </RelativeLayout>

对比最初的activity_test.xml:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="viewModel"
            type="com.example.tsnt.mvvm.TestVM" />
    </data>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/content"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:text="@{viewModel.bottomContent}" />
    </RelativeLayout>
</layout>

可以看出,生成的xml与原始的xml差不多,区别是:
1.去掉了layout标签
2.增加了tag标记,这里是android:tag="layout/activity_test_0"

我们使用DataBinding添加layout标签后的布局文件会经过DataBinding处理成正常的布局文件activity_test.xml与包含绑定信息的文件activity_test-layout.xml

activity_test-layout.xml

位置于app/build/intermediates/data-binding-info/debug目录下:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Layout absoluteFilePath="/Users/zhangxiaozong/Documents/projects/others/AndroidStudy/app/src/main/res/layout/activity_test.xml" directory="layout"
    isMerge="false"
    layout="activity_test" modulePackage="com.example.tsnt">
    <Variables name="viewModel" declared="true" type="com.example.tsnt.mvvm.TestVM">
        <location endLine="7" endOffset="49" startLine="5" startOffset="8" />
    </Variables>
    <Targets>
        <Target tag="layout/activity_test_0" view="RelativeLayout">
            <Expressions />
            <location endLine="26" endOffset="20" startLine="10" startOffset="4" />
        </Target>
        <Target tag="binding_1" view="TextView">
            <Expressions>
                <Expression attribute="android:text" text="viewModel.bottomContent">
                    <Location endLine="25" endOffset="52" startLine="25" startOffset="12" />
                    <TwoWay>false</TwoWay>
                    <ValueLocation endLine="25" endOffset="50" startLine="25" startOffset="28" />
                </Expression>
            </Expressions>
            <location endLine="25" endOffset="55" startLine="20" startOffset="8" />
        </Target>
        <Target id="@+id/content" view="TextView">
            <Expressions />
            <location endLine="18" endOffset="50" startLine="14" startOffset="8" />
        </Target>
    </Targets>
</Layout>

它会告诉我们生成的activity_test.xml对应的tag的数据信息。

ActivityTestBinding

这就是我们生成之后可以直接调用的ViewDataBinding

public class ActivityTestBinding extends android.databinding.ViewDataBinding  {

    private static final android.databinding.ViewDataBinding.IncludedLayouts sIncludes;
    private static final android.util.SparseIntArray sViewsWithIds;
    static {
        sIncludes = null;
        sViewsWithIds = new android.util.SparseIntArray();
        sViewsWithIds.put(R.id.content, 2);
    }
    // views
    public final android.widget.TextView content;
    private final android.widget.RelativeLayout mboundView0;
    private final android.widget.TextView mboundView1;
    // variables
    private com.example.tsnt.mvvm.TestVM mViewModel;
    ……
}

其中可以看到ActivityTestBinding里面持有了一些view的变量,那是不是布局中所有的view都会在ActivityTestBinding中?其实不是,ActivityTestBinding里的view变量有三种类型:
根布局
含有@{}绑定的view
含有id属性的view

BR

位于app/build/generated/source/apt/debug/com/android/databinding/library/baseAdapters目录下:

public class BR {
        public static final int _all = 0;
        public static final int viewModel = 1;
}

DataBinderMapper

位于app/build/generated/source/apt/debug/android/databinding/目录下:

class DataBinderMapper {
    final static int TARGET_MIN_SDK = 16;
    public DataBinderMapper() {
    }
    public android.databinding.ViewDataBinding getDataBinder(android.databinding.DataBindingComponent bindingComponent, android.view.View view, int layoutId) {
        switch(layoutId) {
                case com.example.tsnt.R.layout.activity_test:
                    return com.example.tsnt.databinding.ActivityTestBinding.bind(view, bindingComponent);
        }
        return null;
    }
    android.databinding.ViewDataBinding getDataBinder(android.databinding.DataBindingComponent bindingComponent, android.view.View[] views, int layoutId) {
        switch(layoutId) {
        }
        return null;
    }
    int getLayoutId(String tag) {
        if (tag == null) {
            return 0;
        }
        final int code = tag.hashCode();
        switch(code) {
            case 628154894: {
                if(tag.equals("layout/activity_test_0")) {
                    return com.example.tsnt.R.layout.activity_test;
                }
                break;
            }
        }
        return 0;
    }
    String convertBrIdToString(int id) {
        if (id < 0 || id >= InnerBrLookup.sKeys.length) {
            return null;
        }
        return InnerBrLookup.sKeys[id];
    }
    private static class InnerBrLookup {
        static String[] sKeys = new String[]{
            "_all"
            ,"viewModel"};
    }
}

DataBinderMapper主要提供了从布局文件layoutId到ViewBinding类的映射

绑定ViewModel的过程

首先来看TestActivity主要代码:

public class TestActivity extends AppCompatActivity {

    private ActivityTestBinding dataBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initView();
    }

    private void initView() {
        dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_test);
        dataBinding.content.setText("Hello, databinding!");
        dataBinding.setViewModel(new TestVM());
    }
}

其中关键的代码是:dataBinding.setViewModel(new TestVM());
这里调用的是ActivityTestBinding的setViewModel()

    public void setViewModel(com.example.tsnt.mvvm.TestVM viewModel) {
        this.mViewModel = viewModel;
        synchronized(this) {
            mDirtyFlags |= 0x2L;
        }
        notifyPropertyChanged(BR.viewModel);
        super.requestRebind();
    }

这里通过notifyPropertyChanged(BR.viewModel)去发出ViewModel数据变化的通知。
然后进入ViewDataBinding的requestRebind()

    protected void requestRebind() {
        synchronized (this) {
            if (mPendingRebind) {
                return;
            }
            mPendingRebind = true;
        }
        if (USE_CHOREOGRAPHER) {
            mChoreographer.postFrameCallback(mFrameCallback);
        } else {
            mUIThreadHandler.post(mRebindRunnable);
        }

    }

其中mPendingRebind的定义如下:

    /**
     * Flag indicates that there are pending bindings that need to be reevaluated.
     */
    private boolean mPendingRebind = false;

在requestRebind()中获取同步锁,如果已经处于pendingRebind状态就直接返回,如果不是pendingRebind状态就继续,并且标记mPendingRebind为true

再来看USE_CHOREOGRAPHER的定义:

private static final boolean USE_CHOREOGRAPHER = SDK_INT >= 16;

这里根据系统版本做了点不同的处理,16及以上的,会往mChoreographer发一个mFrameCallback;否则直接往UI线程发一个mRebindRunnable。其实这里俩个分支的结果基本一致,mChoreographer会在界面刷新时执行mRebindRunnable,Choreographer是系统版本16后引入的用于解决UI卡顿的,当收到VSYNC(定时中断)时,在doFrame()里去执行相应的操作。

Android Choreographer和VSYNC机制

接着我们来看mRebindRunnable

    /**
     * Runnable executed on animation heartbeat to rebind the dirty Views.
     */
    private final Runnable mRebindRunnable = new Runnable() {
        @Override
        public void run() {
            synchronized (this) {
                mPendingRebind = false;
            }
            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();
        }
    };

这里又将mPendingRebind置为false,然后对系统版本进行了判断。如果是19及以上时,会判断View是否加载到了屏幕上,如果没有的话,则对这个attach的状态进行监听。这就是我遇到问题的原因,因为View一直没有加载到屏幕上,所以监听也一直没回调。

正常情况下,最终都会执行executePendingBindings()

    /**
     * Evaluates the pending bindings, updating any Views that have expressions bound to
     * modified variables. This <b>must</b> be run on the UI thread.
     */
    public void executePendingBindings() {
        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;
    }

如果mIsExecutingPendingBindings为true,表明当前已经在执行这段代码了,则调用一次requestRebind()

如果hasPendingBindings()返回false,表明当前没有需要需要绑定的数据,则返回不处理。

接下来又进行了一系列判断,如果没被终止,会进入executeBindings(),执行最终绑定。executeBindings()是一个抽象方法,由ViewDataBinding的子类去实现。

看到ActivityTestBinding的executeBindings()

    @Override
    protected void executeBindings() {
        long dirtyFlags = 0;
        synchronized(this) {
            dirtyFlags = mDirtyFlags;
            mDirtyFlags = 0;
        }
        com.example.tsnt.mvvm.TestVM viewModel = mViewModel;
        android.databinding.ObservableField<java.lang.String> bottomContentViewMod = null;
        java.lang.String bottomContentViewMod1 = null;

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



                if (viewModel != null) {
                    // read viewModel.bottomContent
                    bottomContentViewMod = viewModel.bottomContent;
                }
                updateRegistration(0, bottomContentViewMod);


                if (bottomContentViewMod != null) {
                    // read viewModel.bottomContent.get()
                    bottomContentViewMod1 = bottomContentViewMod.get();
                }
        }
        // batch finished
        if ((dirtyFlags & 0x7L) != 0) {
            // api target 1

            android.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView1, bottomContentViewMod1);
        }
    }

这里做的操作就是绑定ViewModel到对应的View,ActivityTestBinding就持有了ViewModel的引用。

然后对ViewModel中绑定的参数注册了监听,最后对View中的数据进行初始化。

接下来看如何注册监听的,进入updateRegistration()

return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);

来看CREATE_PROPERTY_LISTENER的定义:

    /**
     * Method object extracted out to attach a listener to a bound Observable object.
     */
    private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
        @Override
        public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
            return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();
        }
    };

然后继续看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);
        }

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

CREATE_PROPERTY_LISTENER是一个CreateWeakListener对象,它是一个创建WeakPropertyListener的工厂类

WeakPropertyListener内有变量WeakListener,能通过它的getListener()得到WeakListener。

WeakListener的定义又如何呢?

    private static class WeakListener<T> extends WeakReference<ViewDataBinding> {
        private final ObservableReference<T> mObservable;
        protected final int mLocalFieldId;
        private T mTarget;

        public WeakListener(ViewDataBinding binder, int localFieldId,
                ObservableReference<T> observable) {
            super(binder);
            mLocalFieldId = localFieldId;
            mObservable = observable;
        }

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

        public boolean unregister() {
            boolean unregistered = false;
            if (mTarget != null) {
                mObservable.removeListener(mTarget);
                unregistered = true;
            }
            mTarget = null;
            return unregistered;
        }

        public T getTarget() {
            return mTarget;
        }

        protected ViewDataBinding getBinder() {
            ViewDataBinding binder = get();
            if (binder == null) {
                unregister(); // The binder is dead
            }
            return binder;
        }
    }

原来它是一个弱引用,它持有ViewDataBinding以及mTarget(泛型T即VM)

继续看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;
    }

mLocalFieldObservers维持了从localFieldId到WeakListener的映射。先从mLocalFieldObservers取localFieldId对应的WeakListener,如果为null的话,就调用registerTo()进行注册;如果不为空,而且与之前注册过的不一致的话,则重新注册。那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;
        }
        listener.setTarget(observable);
    }

通过CreateWeakListener.create()得到WeakListener之后,registerTo()将WeakListener存储在mLocalFieldObservers中。然后调用WeakListener的setTarget()。

然后调用mObservable的addListener(),mObservable就是在构造WeakListener传入的第三个参数。以上的代码,WeakPropertyListener在构造函数里构造WeakListener是传入的是this,因此mObservable其实就是WeakPropertyListener

在addListener()中,调用Observable的addOnPropertyChangedCallback(),参数为this。BaseObservable实现了接口Observable,在addOnPropertyChangedCallback里,将WeakPropertyListener加入到了mCallbacks(PropertyChangeRegistry)里面

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

这里写图片描述

可以看到这个图与MVVM的架构图有点类似。

这样一来V和VM的联系就通过ViewDatabinding建立起来了。V(Activity)内有ViewDatabinding,而ViewDatabinding里持有各个V(布局中的元素)的引用。ViewDataBinding有VM的变量,而VM内的PropertyChangeRegistry监听实则为WeakPropertyListener,WeakPropertyListener得到的WeakListener能获取到ViewDatabinding的引用。

VM变化如何通知View

我们知道,如果要达到VM变化时自动绑定到View上,有下面俩种方式:

  1. 继承自BaseObservable,在getter上增加@Bindable注解,在setter里增加代码notifyPropertyChanged(BR.xxx)。

  2. 无需继承,需要将属性替换为Observable类,例如ObservableInt、ObservableField等。

这两种方式本质上都是一样的。在第二种方式中,当属性发生变化时,会调用set()进而调用notifyChange(),而notifyChange()notifyPropertyChanged()做的事情都是一样的,都是调用mCallbacks.notifyCallbacks()去发出通知:

    public void set(T value) {
        if (value != mValue) {
            mValue = value;
            notifyChange();
        }
    }

    /**
     * Notifies listeners that all properties of this instance have changed.
     */
    public synchronized void notifyChange() {
        if (mCallbacks != null) {
            mCallbacks.notifyCallbacks(this, 0, null);
        }
    }

    /**
     * Notifies listeners that a specific property has changed. The getter for the property
     * that changes should be marked with {@link Bindable} to generate a field in
     * <code>BR</code> to be used as <code>fieldId</code>.
     *
     * @param fieldId The generated BR id for the Bindable field.
     */
    public void notifyPropertyChanged(int fieldId) {
        if (mCallbacks != null) {
            mCallbacks.notifyCallbacks(this, fieldId, null);
        }
    }

这里的mCallbacks就是刚才添加的WeakPropertyListener

我们一层层往下跟踪,notifyCallbacks(CallbackRegistry) -> notifyRecurse(CallbackRegistry) -> notifyRemainder(CallbackRegistry) -> notifyFirst64(CallbackRegistry),最后到达下面这个方法:

    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.onNotifyCallback(mCallbacks.get(i), sender, arg, arg2);
            }
            bitMask <<= 1;
        }
    }

来看下mNotifier的定义:

    /** The notification mechanism for notifying an event. */
    private final NotifierCallback<C, T, A> mNotifier;

而mNotifier其实就是在BaseObservable构造PropertyChangeRegistry传入的参数:

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

public class PropertyChangeRegistry extends
        CallbackRegistry<Observable.OnPropertyChangedCallback, Observable, Void> {

    private static final CallbackRegistry.NotifierCallback<Observable.OnPropertyChangedCallback, Observable, Void> NOTIFIER_CALLBACK = new CallbackRegistry.NotifierCallback<Observable.OnPropertyChangedCallback, Observable, Void>() {
        @Override
        public void onNotifyCallback(Observable.OnPropertyChangedCallback callback, Observable sender,
                int arg, Void notUsed) {
            callback.onPropertyChanged(sender, arg);
        }
    };

    public PropertyChangeRegistry() {
        super(NOTIFIER_CALLBACK);
    }

    /**
     * Notifies registered callbacks that a specific property has changed.
     *
     * @param observable The Observable that has changed.
     * @param propertyId The BR id of the property that has changed or BR._all if the entire
     *                   Observable has changed.
     */
    public void notifyChange(Observable observable, int propertyId) {
        notifyCallbacks(observable, propertyId, null);
    }
}

因此调用mNotifier.onNotifyCallback实际上就是调用mCallbacks.get(i).onPropertyChanged(),我们进一步看看:

private static class WeakPropertyListener extends Observable.OnPropertyChangedCallback
            implements ObservableReference<Observable> {
    final WeakListener<Observable> mListener;

    // …… 

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

再进入ViewDataBinding的handleFieldChange()

    private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
        boolean result = onFieldChange(mLocalFieldId, object, fieldId);
        if (result) {
            requestRebind();
        }
    }

可以看到onPropertyChanged调用了编译生成的ActivityTestBinding的onFieldChange方法将mDirtyFlags标识为需要重新绑定VM到V上,并且调用requestRebind方法。requestRebind方法前面已经介绍过,最后能调用到ActivityTestBinding的executeBindings()进行界面绑定的工作。

V的变化如何同步到VM

DataBinding在旧版本中是不支持这个功能的,后来才完善了这个功能。

现在对最初的xml进行修改:把android:text="@{viewModel.bottomContent}"改成android:text="@{viewModel.bottomContent}"

在使用双向绑定后,看看生成的ActivityMainBinding有什么变化?可以发现在executeBindings里多了一点代码:

        if ((dirtyFlags & 0x4L) != 0) {
            // api target 1

            android.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.mboundView1, (android.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged)null, (android.databinding.adapters.TextViewBindingAdapter.OnTextChanged)null, (android.databinding.adapters.TextViewBindingAdapter.AfterTextChanged)null, mboundView1androidTe);
        }

在这个方法里调用了setTextWatcher去监听TextView的TextWatcher:

    @BindingAdapter(value = {"android:beforeTextChanged", "android:onTextChanged",
            "android:afterTextChanged", "android:textAttrChanged"}, requireAll = false)
    public static void setTextWatcher(TextView view, final BeforeTextChanged before,
            final OnTextChanged on, final AfterTextChanged after,
            final InverseBindingListener textAttrChanged) {
        final TextWatcher newValue;
        if (before == null && after == null && on == null && textAttrChanged == null) {
            newValue = null;
        } else {
            newValue = new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                    if (before != null) {
                        before.beforeTextChanged(s, start, count, after);
                    }
                }

                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    if (on != null) {
                        on.onTextChanged(s, start, before, count);
                    }
                    if (textAttrChanged != null) {
                        textAttrChanged.onChange();
                    }
                }

                @Override
                public void afterTextChanged(Editable s) {
                    if (after != null) {
                        after.afterTextChanged(s);
                    }
                }
            };
        }
        final TextWatcher oldValue = ListenerUtil.trackListener(view, newValue, R.id.textWatcher);
        if (oldValue != null) {
            view.removeTextChangedListener(oldValue);
        }
        if (newValue != null) {
            view.addTextChangedListener(newValue);
        }
    }

当V发生变化时,会调用textAttrChanged的onChange()即ActivityDataBinding中setTextWatcher()时传入的成员变量的onChange()

    private android.databinding.InverseBindingListener mboundView1androidTe = new android.databinding.InverseBindingListener() {
        @Override
        public void onChange() {
            // Inverse of viewModel.bottomContent.get()
            //         is viewModel.bottomContent.set((java.lang.String) callbackArg_0)
            java.lang.String callbackArg_0 = android.databinding.adapters.TextViewBindingAdapter.getTextString(mboundView1);
            // localize variables for thread safety
            // viewModel.bottomContent
            android.databinding.ObservableField<java.lang.String> bottomContentViewMod = null;
            // viewModel != null
            boolean viewModelObjectnull = false;
            // viewModel
            com.example.tsnt.mvvm.TestVM viewModel = mViewModel;
            // viewModel.bottomContent != null
            boolean BottomContentViewMod1 = false;
            // viewModel.bottomContent.get()
            java.lang.String BottomContentViewMod2 = null;



            viewModelObjectnull = (viewModel) != (null);
            if (viewModelObjectnull) {


                bottomContentViewMod = viewModel.bottomContent;

                BottomContentViewMod1 = (bottomContentViewMod) != (null);
                if (BottomContentViewMod1) {




                    bottomContentViewMod.set((java.lang.String) (callbackArg_0));
                }
            }
        }
    };

在上面textAttrChanged的onChange()回调里,将变动后的值赋值到VM上。这样,V的变化就自动同步到VM上了。

如何避免findViewById

在初始化时,我们是通过DataBindingUtil.setContentView()来建立layout与ViewBinding的联系,因此从这个方法开始分析:

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

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

setContentView()直接调用同名方法,而在这个同名方法内,先设置为activity的contentView,然后将contentView传入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);
    }
}

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

获取到layout中的根布局,并且调用bind方法。bind方法内调用DataBinderMapper的getDataBinder()。DataBinderMapper前面介绍过,也是编译时生成的类,主要是建立layout与ViewBinding之间的映射

    public android.databinding.ViewDataBinding getDataBinder(android.databinding.DataBindingComponent bindingComponent, android.view.View view, int layoutId) {
        switch(layoutId) {
                case com.example.tsnt.R.layout.activity_test:
                    return com.example.tsnt.databinding.ActivityTestBinding.bind(view, bindingComponent);
        }
        return null;
    }

    public static ActivityTestBinding bind(android.view.View view, android.databinding.DataBindingComponent bindingComponent) {
        if (!"layout/activity_test_0".equals(view.getTag())) {
            throw new RuntimeException("view tag isn't correct on view:" + view.getTag());
        }
        return new ActivityTestBinding(bindingComponent, view);
    }

这里根据layoutId调用对应ViewBinding的bind方法,ViewBinding的名称是根据layoutId生成的,例如activity_test对应的ViewBinding名称为ActivityTestBinding,然后在ActivityTestBinding的静态方法bind()中构造了一个ActivityTestBinding对象

    public ActivityTestBinding(android.databinding.DataBindingComponent bindingComponent, View root) {
        super(bindingComponent, root, 1);
        final Object[] bindings = mapBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds);
        this.content = (android.widget.TextView) bindings[2];
        this.mboundView0 = (android.widget.RelativeLayout) bindings[0];
        this.mboundView0.setTag(null);
        this.mboundView1 = (android.widget.TextView) bindings[1];
        this.mboundView1.setTag(null);
        setRootTag(root);
        // listeners
        invalidateAll();
    }

在构造函数里面,我们看到对button、mboundView0、mboundView1进行了强转,因此可以知道mapBindings得到的bindings数组就是View数组。mapBindings方法比较长,我们分成三部分来看看:

① 从view的tag中获取缓存,防止多次初始化

        final int indexInIncludes;
        final ViewDataBinding existingBinding = getBinding(view);
        if (existingBinding != null) {
            return;
        }

② 将view存储在bindings数组内,分为三种情况,与前面生成的ViewBinding内的view变量类型一致,一为根布局,tag以layout开头;二为设置了@{}的,tag以binding开头;三为设置了id属性的。

        Object objTag = view.getTag();
        final String tag = (objTag instanceof String) ? (String) objTag : null;
        boolean isBound = false;
        if (isRoot && tag != null && tag.startsWith("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) {
            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;
                }
            }
        }

③ 这部分判断如果是ViewGroup的话,则判断子View是不是include的,如果是的话,则使用DataBindingUtil.bind进行递归;如果不是include,则直接使用mapBindings进行递归。

        if (view instanceof  ViewGroup) {
            final ViewGroup viewGroup = (ViewGroup) view;
            final int count = viewGroup.getChildCount();
            int minInclude = 0;
            for (int i = 0; i < count; i++) {
                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) {
                                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) {
                    mapBindings(bindingComponent, child, bindings, includes, viewsWithIds, false);
                }
            }
        }
    }

通过这三个步骤,递归得到最后的bindings数组。如果设置了id的,就将view变量设置为public,这样就避免了findViewById的代码。这种方式从性能上比findViewById高效,因为databinding只需要遍历一次view树,而findViewById多次调用会遍历多次。

本文大部分内容参考:DataBinding源码解析

猜你喜欢

转载自blog.csdn.net/sted_zxz/article/details/79593575