深入原始碼學習 android data binding 之:ViewDataBinding

LemonYang發表於2016-12-01

雖然沒有開通專欄之前已經在挖金投稿過了這篇文章,但是我打算寫一個關於android data binding庫的一系列的文章,為了完整性,我還是在這裡重新發布一遍。如果之前已經看過這android data binding 實踐之:ViewDataBinding,那麼可以忽略下面的內容,如果你喜歡的話可以收藏也可以點贊哦!


在前面的文章中,我簡單的介紹了data binding當中的註解的使用,今天主要介紹ViewDataBinding的核心邏輯的實現。

使用過data binding的應該都知道,在編譯之後,這個庫會為我們生成兩種java檔案。

  • BR.java:

    這個檔案主要是給我們在XML檔案中每個標籤設定的variable以及被Bindable註解的屬性新增一個靜態的int型別的索引,在以後對這些variable進行操作的時候都可以使用這個索引id作為引數。

  • ViewDataBinding的相應的子類:

    每一個繫結的XML佈局檔案都會生成一個以該佈局檔名稱作為字首(預設情況下)的ViewDataBinding的相應的子類。在這個檔案中主要的處理邏輯主要包括:

    • 自動化view管理
    • 使用者事件監聽處理
    • 變數管理
    • data binding邏輯流

自動化view管理

當我們在XML佈局檔案中,設定了View的"android:id"屬性之後,在生成的ViewDataBinding子類當中會生成一個宣告為 public final 的屬性值,屬性的名稱就是我們定義的id的值。而對於那些沒有被設定ID的view則會宣告為 private final 。因此當我們給view設定了id之後可以不必再自己宣告和初始化這些view。

首先XML佈局宣告如下

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

    <data>
        <variable
            name="user"
            type="net.uni_unity.databindingdemo.model.ObserVableUser"/>
        <variable
            name="activity"
            type="net.uni_unity.databindingdemo.SecondActivity"/>
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:text="@{user.firstName}"/>
        ... 
        <Button
            android:onClick='@{()->activity.onViewClick("click")}'
            android:id="@+id/btn2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:text="ListenerBinding"/>

    </LinearLayout>
</layout>複製程式碼

在生成的ViewDataBinding子類中,我們會發現:

 // views
    public final android.widget.Button btn1;
    public final android.widget.Button btn2;
    private final android.widget.LinearLayout mboundView0;
    private final android.widget.TextView mboundView1;
    private final android.widget.TextView mboundView2;
    private final android.widget.TextView mboundView3;
    private final android.widget.TextView mboundView4;

    public SecondActivityBinding(android.databinding.DataBindingComponent bindingComponent, View root) {
        ...
        this.btn1 = (android.widget.Button) bindings[5];
        this.btn1.setTag(null);
        this.btn2 = (android.widget.Button) bindings[6];
        this.btn2.setTag(null);
        this.mboundView0 = (android.widget.LinearLayout) bindings[0];
        this.mboundView0.setTag(null);
        this.mboundView1 = (android.widget.TextView) bindings[1];
        this.mboundView1.setTag(null);
        this.mboundView2 = (android.widget.TextView) bindings[2];
        this.mboundView2.setTag(null);
        this.mboundView3 = (android.widget.TextView) bindings[3];
        this.mboundView3.setTag(null);
        this.mboundView4 = (android.widget.TextView) bindings[4];
        this.mboundView4.setTag(null);
        ...
    }複製程式碼

這樣我們就可以直接在activity當中使用這些宣告為public的view了,只要為每個view新增上id之後,連一大堆的"findViewById()"的方法都不用我們去寫了。

事件處理(Event Handling)

data binding可以讓我們通過定義一些表示式處理從view分發的事件,比如點選事件之類的。事件的屬性名字基本由java的listener方法的名字控制(當然也有其他複雜的事件例外)。比如View.OnLongClickListener 擁有一個 onLongClick() 的方法,所以這個事件對應的屬性名稱就是 android:onLongClick。我們有下面兩種方式處理對應的事件。

Method References

使用示例

首先定義我們的事件處理方法

public class SecondActivity extends AppCompatActivity {

    ...
    private void bindData(SecondActivityBinding binding){
        ObserVableUser user=new ObserVableUser("firstName","lastName","observable",11);
        binding.setUser(user);
        binding.setPresenter(this);
    }

    public void simpleClick(View view){
        Log.d("MethodReference","MethodReference click");
    }

}複製程式碼

然後是在我們的佈局檔案中通過呼叫表示式進行繫結

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="presenter"
            type="net.uni_unity.databindingdemo.SecondActivity"/>
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <Button
            android:onClick="@{presenter::simpleClick}"
            android:id="@+id/btn1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:text="MethodReference"/>

   </LinearLayout>
</layout>複製程式碼

使用解析

關於Method References的詳細介紹,我們可以參考官網當中的描述Method References。這裡簡單總結一下關鍵的內容:

  1. 與在XML檔案中定義屬性“android:onClick”,然後在我們對應的activity中定義onClick()方法類似,我們可以通過自定義一個方法,然後在XML中繫結該方法的呼叫表示式,完成事件的繫結。但是不同的是,我們的表示式是在編譯期就被處理的,所以一旦我們表示式呼叫的方法不存在的話或者方法簽名不正確的話就會出現編譯異常
  2. 非常關鍵的一點,我們 自己定義的方法的方法簽名必須與實際的listener回撥方法的簽名保持一致 (即方法的引數型別與個數要一致),否則我們將會在編譯期得到這樣的編譯錯誤

    java.lang.RuntimeException: Found data binding errors.
    / data binding error msg:Listener class android.view.View.OnClickListener with method onClick did not match signature of any method presenter::simpleClick file:/DataBindingDemo/app/src/main/res/layout/second_activity.xml loc:42:31 - 42:52 \ data binding error

  3. XML檔案中繫結的表示式是在編譯期就被處理掉,如果我們希望在事件響應的時候再進行處理,就可以使用下面的 Listener Bindings 的方法

ViewDataBinding當中的實現

當我們使用 Method References 處理事件的時候,data binding library回在 ViewDataBinding 生成下面的處理程式碼

// Listener Stub Implementations
public static class OnClickListenerImpl implements android.view.View.OnClickListener{
    private net.uni_unity.databindingdemo.SecondActivity value;
    public OnClickListenerImpl setValue(net.uni_unity.databindingdemo.SecondActivity value) {
        this.value = value;
        return value == null ? null : this;
    }
    @Override
    public void onClick(android.view.View arg0) {
        this.value.simpleClick(arg0);
    }
}複製程式碼

上面的程式碼可以發現,在 ViewDataBinding 中生成了一個實現了 android.view.View.OnClickListener 事件介面的靜態內部類,而這個類主要做了兩件事情

  1. 新增了一個 setValue() 的方法。方法的引數其實就是我們自己定義的事件回撥方法所在的類的例項。在上面的例項中,我們定義了一個 simpleClick() ,因為我把方法定義在了在activity裡面,所以對應的就是activity的例項。)
  2. 實現了 onClick() 的方法。而方法的實現裡面其實呼叫的就是我們自己定義的那個回撥方法。

而我們會在生成的ViewDataBinding的子類中看到一個 OnClickListenerImpl mAndroidViewViewOnCl 的filed的宣告,而這個物件的初始化就放在了binding呼叫鏈的最後裡面。如下所示:

@Override
protected void executeBindings() {
    ...
    if ((dirtyFlags & 0x28L) != 0) {
        if (activity != null) {
            // read activity::simpleClick
            androidViewViewOnCli = (((mAndroidViewViewOnCl == null) ? (mAndroidViewViewOnCl = new OnClickListenerImpl()) : mAndroidViewViewOnCl).setValue(activity));
        }
    }
    ...
}複製程式碼

從上面的程式碼中我們可以看出,使用 Method References 的方式處理回撥事件,其實就是對我們平常一直使用的 OnClickListener 的一層簡單的包裝而已。

Listener Bindings

通過使用 Listener Bindings 的方式,我們可以在事件回撥的時候再觸發我們所繫結的 lambda 表示式。它的使用方法與 Method References 相似,但是這種方式卻可以使我們具備呼叫已經被編譯成了位元組碼的表示式的能力。
值得注意的是,這個特性只有在Gradle2.0及以後的版本當中才被支援。

使用示例

首先定義我們的事件處理方法

public void onViewClick(String tag){
    Log.d("ListenerBinding","ListenerBinding "+tag);
}複製程式碼

然後是在我們的佈局檔案中通過lambda表示式進行繫結

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="presenter"
            type="net.uni_unity.databindingdemo.SecondActivity"/>
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

         <Button
            android:onClick='@{()->presenter.onViewClick("click")}'
            android:id="@+id/btn2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:text="ListenerBinding"/>

   </LinearLayout>
</layout>複製程式碼

使用解析

關於Listener Bindings的詳細介紹,我們可以參考官網當中的描述Listener Bindings.這裡還是總結一下使用的關鍵點:

  1. 在上面的 Method References 中我們提及到,自定義的方法簽名必須與listener的回撥方法的簽名保持一致。但是 Listener Bindings 卻沒有這樣的要求。它只要求 自定義的方法的返回值與listener的回撥方法的返回值保持一致 即可。
  2. 在定義的 lambda 表示式中,我們可以選擇是否傳入view引數,而且引數的名稱也是可以自己隨意定義的。

ViewDataBinding當中的實現

Listener Bindings 在ViewDataBinding當中的實現要比 Method References 稍微複雜一點。
首先,也是對 android.view.View.OnClickListener 進行了一層包裹,生成了一個實現了點選事件監聽介面的類

public final class OnClickListener implements android.view.View.OnClickListener {
    final Listener mListener;
    final int mSourceId;
    public OnClickListener(Listener listener, int sourceId) {
        mListener = listener;
        mSourceId = sourceId;
    }
    @Override
    public void onClick(android.view.View callbackArg_0) {
        mListener._internalCallbackOnClick(mSourceId , callbackArg_0);
    }
    public interface Listener {
        void _internalCallbackOnClick(int sourceId , android.view.View callbackArg_0);
    }
}複製程式碼

這個類裡面關鍵的點就是內部又定義了一個Listener的介面,並且點選事件的最終呼叫的就是介面裡面的 _internalCallbackOnClick() 方法。那麼根據上面的 Method References 的實現套路,我們大概能猜到我們的 ViewDataBinding 應該實現了上面的介面。?

public class SecondActivityBinding extends android.databinding.ViewDataBinding implements android.databinding.generated.callback.OnClickListener.Listener {

    ...
    public final void _internalCallbackOnClick(int sourceId , android.view.View callbackArg_0) {
        // localize variables for thread safety
        // presenter
        net.uni_unity.databindingdemo.SecondActivity presenter = mPresenter;
        // presenter != null
        boolean presenterObjectnull = false;
        presenterObjectnull = (presenter) != (null);
        if (presenterObjectnull) {
            presenter.onViewClick("click");
        }
    }
    ...
}複製程式碼

在上面的程式碼中 presenter.onViewClick("click") 方法裡面的字串是我們的lambda表示式裡面傳入的引數。

那麼我們是在哪個地方完成了view的監聽事件繫結的呢?
首先,我們在生成的 ViewDataBinding 子類裡面的構造方法看到了如下的初始化程式碼

public SecondActivityBinding(android.databinding.DataBindingComponent bindingComponent, View root) {
        super(bindingComponent, root, 3);
        final Object[] bindings = mapBindings(bindingComponent, root, 7, sIncludes, sViewsWithIds);
        ...
        this.btn2 = (android.widget.Button) bindings[6];
        this.btn2.setTag(null);
        // listeners
        mCallback1 = new android.databinding.generated.callback.OnClickListener(this, 1);
        invalidateAll();
    }複製程式碼

上面的 mCallback1 初始化之後呼叫了 invalidateAll() 這個方法,關於這個方法的呼叫鏈我們會在後面繼續分析,我們這裡先把結果說出來,在呼叫鏈的最後會觸發 executeBindings() 的呼叫,而在這個方法的裡面我們能夠發現:

@Override
protected void executeBindings() {
    ...
    if ((dirtyFlags & 0x20L) != 0) {
        // api target 1
        this.btn2.setOnClickListener(mCallback1);
    }
    ...
}複製程式碼

看到這裡,大概每個看官都能夠明白了,其實Listener BindingsMethod References 非常相似,兩個都是對原來的listener的介面實現進行了一層包裹,最終呼叫在 onClick() 事件裡面呼叫我們自己方法。

上面還有一個問題被忽略了,那就是在例項化 mCallback 的時候,需要傳入一個稱為 sourceId 的值。關於這個值是怎麼回事,我們會在以後的 android data binding實踐之:註解處理流剖析 一文中再進行詳細的討論。

變數管理

關於這一塊的內容,有一篇文章分析的挺好的,感興趣的可以看一下。

Android Data Binding 系列(二) -- Binding與Observer實現原理

我們在XML佈局檔案中繫結的每一個variable都會在對應的ViewDataBinding實現子類中生成一個對應的setter方法。不管對應的variable是一個簡單的Java bean物件,還是一個實現了observable的物件,抑或是使用了ObservableField的物件,基本上都是一樣的(存在細微不同,下面會提及)。看下面程式碼的實際例子:

//這段程式碼顯示的是我們在XML檔案中繫結的一個publicUser物件生成的setter方法
//這裡的publicUser是一個整合了observable的物件
public void setPublicUser(net.uni_unity.databindingdemo.model.PublicUser publicUser) {
    //更新監聽的註冊
    //在這個方法裡面,確保不重複監聽,會先移除再新增觀察監聽
    //對於ObservableField,這個監聽註冊的更新是放在executeBindings()方法當中的,也即requestRebind()呼叫鏈的最後一步
    updateRegistration(1, publicUser);
    this.mPublicUser = publicUser;
    //通過mDirtyFlags標識變數(關於這個mDirtyFlags,自己不是很理解)
    synchronized(this) {
        mDirtyFlags |= 0x2L;
    }
    //介面回撥,通知屬性發生了變化
    notifyPropertyChanged(BR.publicUser);
    //requestRebind()方法之後會經過一系列的引用鏈之後執行再次繫結,實現view的重新整理
    super.requestRebind();
}複製程式碼

從上面的分析可以知道,data binding當中任意的一個屬性的改變,基本都會經歷下面的幾個步驟:

  • 監聽的註冊(對應的是updateRegistration()方法)
  • 基本的賦值操作
  • 鎖定mDirtyFlags標識變數
  • 監聽介面回撥(對應的是notifyPropertyChanged()方法)
  • 執行重新繫結(requestRebind()方法)

監聽的註冊

上面的 updateRegistration(int, Observable)最終呼叫的是另一個一個私有的方法 updateRegistration(int, Object,CreateWeakListener listenerCreator) ,程式碼如下所示:

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


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;
    }
    // target是我們的監聽介面的監聽的物件,當前後監聽物件一致的時候,無需任何操作
    if (listener.getTarget() == observable) {
        return false;//nothing to do, same object
    }
    // 否則線取消原來的監聽,再進行重新註冊新的監聽
    unregisterFrom(localFieldId);
    registerTo(localFieldId, observable, listenerCreator);
    return true;
}複製程式碼

在上面的呼叫過程中,傳入了一個名為 CREATE_PROPERTY_LISTENER 的CreateWeakListener物件,而它在ViewDataBinding定義的如下:

private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
    @Override
    public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
        return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();
    }
};複製程式碼

CREATE_PROPERTY_LISTENER 只是一個例項化的介面物件,在registerTo()方法中,通過介面物件的create()方法可以建立出一個弱引用的WeakListener物件,這個物件來自於 WeakPropertyListener 的field值。

registerTo()方法主要是將listener繫結到Observable物件上。繫結時,會呼叫listener.setTarget()將Observable物件傳給WeakPropertyListener例項,然後,WeakPropertyListener會為Observable物件新增OnPropertyChangedCallback
程式碼如下所示:

//ViewDataBinding中的監聽註冊入口方法
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);
}

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

    // 將Observable物件傳遞給WeakPropertyListener的例項mObservable
    // 因為WeakPropertyListener實現了ObservableReference的介面
    // 在新建WeakListener的例項的時候,其實是獲取WeakPropertyListener的mListener(參考前面的CREATE_PROPERTY_LISTENER)
    public void setTarget(T object) {
        unregister();
        mTarget = object;
        if (mTarget != null) {
            mObservable.addListener(mTarget);
        }
    }
    ...
}

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?
        }
        // 註冊的監聽介面的回撥最後會來到這個地方
        // 而handleFieldChange()的方法實現會在編譯期生成的ViewDataBinding當中實現
        binder.handleFieldChange(mListener.mLocalFieldId, sender, propertyId);
    }
}

//addOnPropertyChangedCallback()是由BaseObservable裡面定義的,如下所示:
public class BaseObservable implements Observable {
    ...

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

    @Override
    public synchronized void removeOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
        if (mCallbacks != null) {
            mCallbacks.remove(callback);
        }
    }
    ...
}複製程式碼

前面涉及到的介面和方法比較多,下面附上一張關於這一塊的類圖:

深入原始碼學習 android data binding 之:ViewDataBinding

前面說了那麼多,終於把監聽介面註冊完了。那麼接下來繼續看一下回撥的流程

監聽介面回撥

設定或更新Observable物件(setter方法被呼叫)的時候,都會呼叫 notifyPropertyChanged() 或者 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) {
    // mCallbacks 是 PropertyChangeRegistry物件,在addOnPropertyChangedCallback 時例項化
    // 如果註冊了Observable物件監聽,那麼mCallbacks不為null
    if (mCallbacks != null) {
        mCallbacks.notifyCallbacks(this, fieldId, null);
    }
}

//PropertyChangeRegistry繼承了CallbackRegistry,
//上面的notifyCallbacks()呼叫回進入CallbackRegistry的notifyCallbacks()
public class CallbackRegistry<C, T, A> implements Cloneable {
    ...
    private void notifyCallbacks(T sender, int arg, A arg2, int startIndex, int endIndex, long bits) {

        long bitMask = 1L;
        for(int i = startIndex; i < endIndex; ++i) {
            if((bits & bitMask) == 0L) {
                // mNotifier 是例項化PropertyChangeRegistry時建立的
                // mNotifier 即CallbackRegistry.NotifierCallback
                this.mNotifier.onNotifyCallback(this.mCallbacks.get(i), sender, arg, arg2);
            }
            bitMask <<= 1;
        }
    }
    ...
}

// onNotifyCallback()是CallbackRegistry.NotifierCallback的一個抽象方法
// 它的實現是在PropertyChangeRegistry當中
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 是為Observable物件新增的OnPropertyChangedCallback,即WeakPropertyListener
                // 而上面的WeakPropertyListener最後回在onPropertyChanged()中呼叫生成的ViewDataBinding的子類的handleFieldChange()
            callback.onPropertyChanged(sender, arg);
        }
    };
    ...
}複製程式碼

經過上面的幾番周折,終於完成了監聽介面的回撥操作。

在我們的變數的setter方法當中還有一個環節就是呼叫 requestRebind() 。這個方法的內容,我放在下面的data binding邏輯流當中進行闡述。

data binding邏輯流

在我們編譯期生成的ViewDataBinding的生成子類的構造方法中,首先會通過 mapBindings() 完成繫結的view的統計,緊接著就是對相應的view進行初始化。構造方法的最後會有一個 invalidateAll() 的呼叫。程式碼如下所示:

@Override
public void invalidateAll() {
    synchronized(this) {
            mDirtyFlags = 0x20L;
    }
    // 這個方法也是我們呼叫setter方法更新屬性值的時候會呼叫的重新繫結的操作的方法
    requestRebind();
}

protected void requestRebind() {
    synchronized (this) {
        if (mPendingRebind) {
            return;
        }
        mPendingRebind = true;
    }
    // 這裡是根據系統版本的不同之行的不同的頁面重新整理策略
    // USE_CHOREOGRAPHER = SDK_INT >= 16
    if (USE_CHOREOGRAPHER) {
        mChoreographer.postFrameCallback(mFrameCallback);
    } else {
        mUIThreadHandler.post(mRebindRunnable);
    }
}

// 上面這個我們著重關注mRebindRunnable的實現,它是我們執行重新繫結的核心任務

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

// 顯然上面經過一番檢查之後,最終呼叫了executePendingBindings(),這就是真正的大boss所在了

/**
 * 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;
    //mRebindCallbacks是一個CallbackRegistry物件的例項,這裡是一個重新繫結事件的回撥的集合
    //CallbackRegistry這個物件看完前面的應該感覺到很熟悉,我們的監聽介面回撥也是跟它的一個子類打交道的
    if (mRebindCallbacks != null) {
        mRebindCallbacks.notifyCallbacks(this, REBIND, null);

        // The onRebindListeners will change mPendingHalted
        if (mRebindHalted) {
            mRebindCallbacks.notifyCallbacks(this, HALTED, null);
        }
    }
    if (!mRebindHalted) {
        // 這是在編譯期的生成子類當中會實現的一個方法
        // 在這個方法裡面完成了view的點選事件繫結、variable的賦值、view的操作(包括ObservableField物件的監聽註冊)等佈局頁面與資料之間的繫結操作
        executeBindings();
        if (mRebindCallbacks != null) {
            mRebindCallbacks.notifyCallbacks(this, REBOUND, null);
        }
    }
    mIsExecutingPendingBindings = false;
}

// 根據前面的分析,我們的mRebindCallbacks通過notifyCallbacks()完成回撥的通知
// 下面看一下重綁回撥實現的CallbackRegistry.NotifierCallbac物件

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;
            }
        }
    };複製程式碼

data binding的邏輯流還有很多的細節,這裡暫時不做具體的展開,只是掌握基本的事件傳遞的流程。相信仔細看上面的的程式碼的註釋的話,基本都能夠掌握到這個邏輯流的線索。

寫在後面

寫這篇分析前前後後看了n多遍ViewDataBinding這個類以及編譯期生成的相應子類,程式碼量不算輕,好幾次都把自己搞得一臉懵逼,而且也知道該用怎樣的邏輯去表述自己的理解。最後自己將裡面的內容進行了分塊,大概就像上面闡述的那樣。

另外這篇文章也有幾個問題沒有解決:

  • dirtyFlags這個變數的使用
  • 關於view data binding當中的事件回撥機制沒有進行仔細的展開,即CallbackRegistry的具體實現細節和邏輯,這個之後後另外再開一篇文章進行闡述
  • data binding的邏輯流過程中會存在很多細節的問題也沒有繼續展開

關於上面這幾個問題,對於邏輯流的細節,我覺得只要我們能夠掌握主線,基本上在使用的過程中估計也能明白不少問題了。

另外關於這個data binding庫的註解處理也是非常的漂亮的,自己會再另外一篇文章進行簡要的闡述。

上面可能還有不少表述不清的地方,歡迎有興趣的一起討論,一起學習。感謝你寶貴的時間閱讀這篇文章!

相關文章