关闭

DataBinding源码解析

标签: 源码解析双向绑定MVVM
1098人阅读 评论(8) 收藏 举报

DataBinding是谷歌15年推出的library。DataBinding支持双向绑定,能大大减少绑定app逻辑与layout文件的“胶水代码”,例如setText、findViewById等代码。双向绑定,指的是将数据与界面绑定起来,当数据发生变化时会体现在界面上,反过来界面内容变化也会同步更新到数据上,使用DataBinding能轻松实现MVVM模式。本文在分析DataBinding时,将用ViewModel(VM)表示数据,用View(V)表示界面。

文初准备

本文着重于对DataBinding原理的分析,因此关于MVVM、DataBinding使用过程不做介绍。推荐一些相关的文章:

如何支持双向绑定?

DataBinding生成的代码介绍

在分析双向绑定的原理之前,先了解DataBinding生成的主要代码,有activity_main.xml、activity_main-layout.xml、ActivityMainBinding.java、BR.java、DataBinderMapper.java等等。

activity_main.xml

位置:
app/build/intermediates/data-binding-layout-out/debug/layout/activity_main.xml


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

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

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:tag="binding_1"     />

    </RelativeLayout>

可以看出,activity_main.xml与不用DataBinding前的布局文件差不多,唯一的区别是加了tag标记。 根布局的tag为layout/activity_main_0,而button的tag为binding_0。那我们使用DataBinding时的data标签以及里面的import与variable标签在哪里?其实它们都在下面介绍得activity_main-layout.xml里面。使用DataBinding重写后的布局文件无法被“理解”,因此需要经过DataBinding处理成正常的布局文件activity_main.xml与包含绑定信息的文件activity_main-layout.xml。

activity_main-layout.xml

位置:
app/build/intermediates/data-binding-info/debug/activity_main-layout.xml

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

<Layout layout="activity_main" modulePackage="com.example.databindingdemo" absoluteFilePath="/Users/linhaiyang807/Desktop/studioWorkspace/DataBindingDemo/app/src/main/res/layout/activity_main.xml" directory="layout" isMerge="false">
  <Variables declared="true" type="User" name="user">
    <location startLine="7" startOffset="8" endLine="9" endOffset="25"/>
  </Variables>
  <Imports type="com.example.databindingdemo.User" name="User">
    <location startLine="5" startOffset="8" endLine="5" endOffset="57"/>
  </Imports>CREATE_PROPERTY_LISTENER
  <Targets>
    <Target tag="layout/activity_main_0" view="RelativeLayout">
      <Expressions/>
      <location startLine="12" startOffset="4" endLine="22" endOffset="20"/>
    </Target>
    <Target id="@+id/button" tag="binding_1" view="Button">
      <Expressions>
        <Expression text="user.name" attribute="android:text">
          <Location startLine="20" startOffset="12" endLine="20" endOffset="38"/>
          <TwoWay>false</TwoWay>
          <ValueLocation startLine="20" startOffset="28" endLine="20" endOffset="36"/>
        </Expression>
      </Expressions>
      <location startLine="16" startOffset="8" endLine="20" endOffset="41"/>
    </Target>
  </Targets>
</Layout>

activity_main-layout.xml内包含了数据绑定相关的信息。Variables与Imports和布局文件中的variable与import标签对应,Targets标签则告诉VM变化时对应的V、绑定关系是单向还是双向的以及DataBinding的表达式Expressions。

ActivityMainBinding.java

位置:
app/build/generated/source/apt/debug/com/example/databindingdemo/databinding/ActivityMainBinding.java

public class ActivityMainBinding extends android.databinding.ViewDataBinding  {

    private static final android.databinding.ViewDataBinding.IncludedLayouts sIncludes;
    private static final android.util.SparseIntArray sViewsWithIds;
    static {
        sIncludes = null;
        sViewsWithIds = null;
    }
    // views
    public final android.widget.Button button;
    private final android.widget.RelativeLayout mboundView0;
    // variables
    private com.example.databindingdemo.User mUser;
    // values

    // …… 
}

可以看到ActivityMainBinding里面持有了一些view的变量,那是不是布局中所有的view都会在ActivityMainBinding中?其实不是,ActivityMainBinding里的view变量有三种类型,① 根布局 ② 含有@{}绑定的view ③ 含有id属性的view。 设置了id的view对应的变量为public的,可以通过ActivityMainBinding直接访问,避免了findViewById的过程,具体原理将在下面分析。ActivityMainBinding内部主要负责双向绑定的功能,留在下面分析。

BR

位置:
app/build/generated/source/apt/debug/com/android/databinding/library/baseAdapters/BR.java

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

BR文件存储了绑VM的id,功能与R文件类似。

DataBinderMapper

位置:
app/build/generated/source/apt/debug/android/databinding/DataBinderMapper.java

class DataBinderMapper  {
    final static int TARGET_MIN_SDK = 21;
    public DataBinderMapper() {
    }
    public android.databinding.ViewDataBinding getDataBinder(android.databinding.DataBindingComponent bindingComponent, android.view.View view, int layoutId) {
        switch(layoutId) {
                case com.example.databindingdemo.R.layout.activity_main:
                    return com.example.databindingdemo.databinding.ActivityMainBinding.bind(view, bindingComponent);
        }
        return null;
    }
    // …… 
}

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

初始化绑定

在初始化时我们会调用下面的代码:

final ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.setUser(user);

进入setUser:

public void setUser(User User) {
    this.mUser = User;
    synchronized(this) {
        this.mDirtyFlags |= 2L;
    }

    this.notifyPropertyChanged(1);
    super.requestRebind();
}

mDirtyFlags用于表示哪个属性发生变化,notifyPropertyChanged(1)实则为notifyPropertyChanged(BR.user),顾名思义,是发出user数据变化的通知。看看requestRebind是干什么的:

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

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

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

重置mPendingRebind为false。如果api19及以上时,判断rootView是否attach到window上,如果没有的话,则对这个attach的状态进行监听。最终都会执行executePendingBindings(),继而调用executeBindingsInternal()方法。

/**
  * Evaluates the pending bindings without executing the parent bindings.
  */
private void executeBindingsInternal() {
    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方法返回是否有数据需要绑定。如果当前没有需要需要绑定的数据,则返回不处理。接下来通知所有的RebindCallback,RebindCallback可以通过ActivityMainBinding.addOnRebindCallback设置。RebindCallback里可以把mRebindHalted置为true,以终止后面的executeBindings()方法。如果被终止了,同样HALTED事件也会通知给所有的RebindCallback。

那RebindCallback如何设置mRebindHalted呢?我们看看mRebindCallbacks长什么样:

/**
  * The collection of OnRebindCallbacks.
  */
private CallbackRegistry<OnRebindCallback, ViewDataBinding, Void> mRebindCallbacks;

/**
  * Add a listener to be called when reevaluating dirty fields. This also allows automatic
  * updates to be halted, but does not stop explicit calls to {@link #executePendingBindings()}.
  *
  * @param listener The listener to add.
  */
public void addOnRebindCallback(OnRebindCallback listener) {
    if (mRebindCallbacks == null) {
        mRebindCallbacks = new CallbackRegistry<OnRebindCallback, ViewDataBinding, Void>(REBIND_NOTIFIER);
    }
    mRebindCallbacks.add(listener);
}

在构造mRebindCallbacks会传入一个REBIND_NOTIFIER,如下面代码所示,在进行rebind操作时,如果callback.onPreBind返回false,则会将sender.mRebindHalted置为true。这样一来executeBindings就不会被执行了。

private static final CallbackRegistry.NotifierCallback<OnRebindCallback, ViewDataBinding, Void>
    REBIND_NOTIFIER = new NotifierCallback<OnRebindCallback, ViewDataBinding, Void>() {
    @Override
    public void onNotifyCallback(OnRebindCallback callback, ViewDataBinding sender, int mode,
            Void arg2) {
        switch (mode) {
            case REBIND:
                if (!callback.onPreBind(sender)) {
                    sender.mRebindHalted = true;
                }
                break;
            case HALTED:
                callback.onCanceled(sender);
                break;
            case REBOUND:
                callback.onBound(sender);
                break;
        }
    }
};

如果mRebindHalted没有被置为false,就是执行executeBindings方法。executeBindings是一个抽象的方法,具体实现在编译时生成的ActivityMainBinding里。

这样一来我们完成了下面这个过程:

V与VM绑定

接下来看看生成的executeBindings代码里做了什么?

@Override
protected void executeBindings() {
    long dirtyFlags = 0;
    synchronized(this) {
        dirtyFlags = mDirtyFlags;
        mDirtyFlags = 0;
    }
    android.databinding.ObservableField<java.lang.String> userName = null;
    java.lang.String userNameGet = null;
    com.example.databindingdemo.User user = mUser;

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



            if (user != null) {
                // read user.name
                userName = user.getName();
            }
            updateRegistration(0, userName);


            if (userName != null) {
                // read user.name.get()
                userNameGet = userName.get();
            }
    }
    // batch finished
    if ((dirtyFlags & 0x7L) != 0) {
        // api target 1

        android.databinding.adapters.TextViewBindingAdapter.setText(this.button, userNameGet);
    }
}

这里面的代码比较简单,除了对界面进行赋值,还调用了updateRegistration方法。第一个入参localFieldId与第二个入参的userName一一对应。

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

updateRegistration第三个参数传了CREATE_PROPERTY_LISTENER,我们先看看CREATE_PROPERTY_LISTENER是什么,再分析updateRegistration方法。

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

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

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, sReferenceQueue);
        mLocalFieldId = localFieldId;
        mObservable = observable;
    }

    // …… 
}

从上面知道CREATE_PROPERTY_LISTENER是一个CreateWeakListener对象,CreateWeakListener.create()能得到WeakPropertyListener,WeakPropertyListener内有变量WeakListener,WeakListener是个弱引用,持有ViewDataBinding以及Observable(即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方法:

private static class WeakListener<T> extends WeakReference<ViewDataBinding> {

    private final ObservableReference<T> mObservable;
    // ......

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

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

然后调用mObservable的addListener方法,mObservable就是在构造WeakListener传入的第三个参数。根据下面的代码,WeakPropertyListener在构造函数里构造WeakListener是传入的是this,因此mObservable其实就是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 void addListener(Observable target) {
        target.addOnPropertyChangedCallback(this);
    }
    // ......
}

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

V与VM练习图

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

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

VM变化如何通知View

前面我们已经讲过了V和VM是如何建立起联系的,接下来我们跟着流程看看具体如何?

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

  • 继承自BaseObservable,在getter上增加@Bindable注解,在setter里增加代码notifyPropertyChanged(BR.xxx)。
  • 无需继承,需要将属性替换为Observable类,例如ObservableInt、ObservableField等。

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

/**
  * Notifies listeners that all properties of this instance have changed.
  */
public void notifyChange() {
    synchronized (this) {
        if (mCallbacks == null) {
            return;
        }
    }
    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) {
    synchronized (this) {
        if (mCallbacks == null) {
            return;
        }
    }
    mCallbacks.notifyCallbacks(this, fieldId, null);
}

mCallbacks添加监听的过程上面我们已经分析过了,也可以通过函数调用,find usages,一层层往上寻找。addOnPropertyChangedCallback(BaseObservable) -> addListener(WeakPropertyListener) -> setTarget(WeakListener)。到这里,就和上面的registerTo方法“会师”了,因此在调用binding.setUser(user)绑定时,就在Observable上加了监听,观察者就是WeakPropertyListener。当VM发生变化时,会通过mCallbacks.notifyCallbacks将变化发送出去。

那当VM发生变化时,notifyCallbacks又是如何将变化发送出去呢?

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

/**
  * Notify callbacks from startIndex to endIndex, using bits as the bit status
  * for whether they have been removed or not. bits should be from mRemainderRemoved or
  * mFirst64Removed. bits set to 0 indicates that all callbacks from startIndex to
  * endIndex should be notified.
  *
  * @param sender The originator. This is an opaque parameter passed to
  * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
  * @param arg An opaque parameter passed to
  * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
  * @param arg2 An opaque parameter passed to
  * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
  * @param startIndex The index into the mCallbacks to start notifying.
  * @param endIndex One past the last index into mCallbacks to notify.
  * @param bits A bit field indicating which callbacks have been removed and shouldn't
  *             be notified.
  */
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其实就是在BaseObservable构造PropertyChangeRegistry传入的参数。

public CallbackRegistry(NotifierCallback<C, T, A> notifier) {
    mNotifier = notifier;
}

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

因此调用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);
    }
}

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

V的变化如何同步到VM

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

<Button
    android:id="@+id/button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@={user.name}" />

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

@Override
protected void executeBindings() {
    // …… 
    if ((dirtyFlags & 0x4L) != 0) {
        // api target 1

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

在这个方法里调用了setTextWatcher去监听Button的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 onTextChanged(CharSequence s, int start, int before, int count) {
// …...
                if (textAttrChanged != null) {
                    textAttrChanged.onChange();
                }
            }

            // …...
        };
    }
    // …...
    if (newValue != null) {
        view.addTextChangedListener(newValue);
    }
}

当V发生变化时,会调buttonandroidTextAttrChanged的onChange方法。

// Inverse Binding Event Handlers
private android.databinding.InverseBindingListener buttonandroidTextAttrChanged = new android.databinding.InverseBindingListener() {
    @Override
    public void onChange() {
        // Inverse of user.name.get()
        //         is user.name.set((java.lang.String) callbackArg_0)
        java.lang.String callbackArg_0 = android.databinding.adapters.TextViewBindingAdapter.getTextString(button);
        // localize variables for thread safety
        // user.name
        android.databinding.ObservableField<java.lang.String> userName = null;
        // user.name != null
        boolean userNameJavaLangObjectNull = false;
        // user != null
        boolean userJavaLangObjectNull = false;
        // user
        com.example.databindingdemo.User user = mUser;
        // user.name.get()
        java.lang.String userNameGet = null;

        userJavaLangObjectNull = (user) != (null);
        if (userJavaLangObjectNull) {

            userName = user.getName();

            userNameJavaLangObjectNull = (userName) != (null);
            if (userNameJavaLangObjectNull) {
                userName.set(((java.lang.String) (callbackArg_0)));
            }
        }
    }
};

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

如何避免findViewById

在介绍双向绑定的原理时,提过“在layout中设置了id属性的,可以直接通过ViewBinding.xxx进行访问”,这种方式避免了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.databindingdemo.R.layout.activity_main:
                return com.example.databindingdemo.databinding.ActivityMainBinding.bind(view, bindingComponent);
    }
    return null;
}

这里根据layoutId调用对应ViewBinding的bind方法,ViewBinding的名称是根据layoutId生成的,例如activity_main对应的ViewBinding名称为ActivityMainBinding。

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

bind方法里面构造了一个ActivityMainBinding对象。

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

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

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

private static void mapBindings(DataBindingComponent bindingComponent, View view,
        Object[] bindings, IncludedLayouts includes, SparseIntArray viewsWithIds,
        boolean isRoot) {
    // ……
    final ViewDataBinding existingBinding = getBinding(view);
    if (existingBinding != null) {
        return;
    }
    final String tag = (String) view.getTag();
    // ……
}

static ViewDataBinding getBinding(View v) {
    if (v != null) {
        if (USE_TAG_ID) {
            return (ViewDataBinding) v.getTag(R.id.dataBinding);
        } else {
            final Object tag = v.getTag();
            if (tag instanceof ViewDataBinding) {
                return (ViewDataBinding) tag;
            }
        }
    }
    return null;
}

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

private static void mapBindings(DataBindingComponent bindingComponent, View view,
        Object[] bindings, IncludedLayouts includes, SparseIntArray viewsWithIds,
        boolean isRoot) {
    // ……
    if (isRoot && tag != null && tag.startsWith("layout")) { // 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)) { // tag以binding_开头
        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) { // 设置了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;
            }
        }
    }
    // ……
}

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

private static void mapBindings(DataBindingComponent bindingComponent, View view,
        Object[] bindings, IncludedLayouts includes, SparseIntArray viewsWithIds,
        boolean isRoot) {
    // ……
    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) { // include的layout,使用DataBindingUtil.bind进行递归
                String childTag = (String) child.getTag();
                if (childTag != null && 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) { // 不是include的layout ,使用mapBindings进行递归
                mapBindings(bindingComponent, child, bindings, includes, viewsWithIds, false);
            }
        }
    }
    // ……
}

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

如何生成代码?

databinding在编译时会生成代码,利用了apt(annotation-processing-tool),apt在ButterKnife与EventBus3都有运用到,有兴趣的可以看看之前分析的文章:

databinding仓库在https://android.googlesource.com/platform/frameworks/data-binding/+/master

这里对主要的脉络分析下,不细入研究。

先从注解处理器开始,对应的类为 compiler / src / main / java / android / databinding / annotationprocessor / ProcessDataBinding.java

@Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        if (mProcessingSteps == null) {
            initProcessingSteps();
        }
        final BindingBuildInfo buildInfo = BuildInfoUtil.load(roundEnv);
        if (buildInfo == null) {
            return false;
        }
        boolean done = true;
        for (ProcessingStep step : mProcessingSteps) {
            try {
                done = step.runStep(roundEnv, processingEnv, buildInfo) && done;
            } catch (JAXBException e) {
                L.e(e, "Exception while handling step %s", step);
            }
        }
        // …… 
        return done;
    }

process内循环执行了ProcessingStep数组,ProcessingStep数组内有ProcessMethodAdapters、ProcessExpressions、ProcessBindable三个。

mProcessingSteps = Arrays.asList(
            new ProcessMethodAdapters(),
            new ProcessExpressions(),
            new ProcessBindable()
    );

① ProcessMethodAdapters负责处理@BindingAdapter 、@BindingMethods、@BindingConversion等注解,并将相关的信息通过SetterStore保存起来。

public class ProcessMethodAdapters extends ProcessDataBinding.ProcessingStep {
    // …… 
    @Override
    public boolean onHandleStep(RoundEnvironment roundEnv,
            ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo) {
        // …… 
        addBindingAdapters(roundEnv, processingEnvironment, store);
        addRenamed(roundEnv, store);
        addConversions(roundEnv, store);
        addUntaggable(roundEnv, store);
        addInverseAdapters(roundEnv, processingEnvironment, store);
        addInverseMethods(roundEnv, store);
        try {
            store.write(buildInfo.modulePackage(), processingEnvironment);
        } catch (IOException e) {
            L.e(e, "Could not write BindingAdapter intermediate file.");
        }
        return true;
    }
    // …… 
}

② ProcessExpressions负责处理layout文件,将layout文件转换为前面所提到的activity_main.xml(正常布局)和activity_main-layout.xml(包含绑定信息),总结起来就是解析xml布局,解析里面的layout、import、variables等标签,然后生成文件。这里就不细入了,代码量比较大。

③ ProcessBindable负责生成BR类,例如BR.user等。

private void initProcessingSteps() {
    // …… 
    Callback dataBinderWriterCallback = new Callback() {
        CompilerChef mChef;
        BRWriter mBRWriter;
        boolean mLibraryProject;
        int mMinSdk;
        // …… 
       private void considerWritingMapper() {
           if (mLibraryProject || mChef == null || mBRWriter == null) {
               return;
           }
           mChef.writeDataBinderMapper(mMinSdk, mBRWriter);
        }
        @Override
        public void onBrWriterReady(BRWriter brWriter) {
            Preconditions.checkNull(mBRWriter, "Cannot set br writer twice");
            mBRWriter = brWriter;
            considerWritingMapper();
        }
    };
    //…… 
}

生成好BR类之后,会回调considerWritingMapper方法。最后利用compiler / src / main / kotlin / android / databinding / tool / writer / DataBinderWriter.kt与compiler / src / main / kotlin / android / databinding / tool / writer / LayoutBinderWriter.kt生成DataBinderMapper与ViewBinding类。这里也不细入了,是用kotlin写的。

DataBinding三问

到这里,关于DataBinding的源码解析就结束了。那么假如我是面试官,问你一下三个问题,你们如何作答?

  • Databinding如何避免findViewById的?
  • DataBinding中是如何实现当V的变化同步更新到VM的?
  • DataBinding中是如何实现将VM的数据绑定到V的?
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:8522次
    • 积分:201
    • 等级:
    • 排名:千里之外
    • 原创:10篇
    • 转载:0篇
    • 译文:0篇
    • 评论:14条
    最新评论