Android Data Binding 系列(二) -- Binding與Observer實(shí)現(xiàn)原理

寫在前面

上篇文章 Android Data Binding 系列(一) -- 詳細(xì)介紹與使用 介紹了 Data Binding 的基礎(chǔ)及其用法遍蟋,本文接上篇浅妆,結(jié)合DataBindingDemo 來學(xué)習(xí)下 Data Binding 的實(shí)現(xiàn)剃毒。

綁定實(shí)現(xiàn)

Activity在inflate layout時(shí)胰坟,通過DataBindingUtil來生成綁定涕侈,從代碼看,是遍歷contentView得到View數(shù)組對(duì)象秒旋,然后通過數(shù)據(jù)綁定library生成對(duì)應(yīng)的Binding類,含Views诀拭、變量迁筛、listeners等。生成類位于
build/intermediates/classes/debug/...package.../databinding/xxx.java 下,具體如何生成這里暫不作深入细卧。

綁定過程

  • 首先尉桩,會(huì)在父類(ViewDataBinding)中實(shí)例化回調(diào)或Handler,用于之后的綁定操作贪庙;
private static final boolean USE_CHOREOGRAPHER = SDK_INT >= 16;

if (USE_CHOREOGRAPHER) {
    mChoreographer = Choreographer.getInstance();
    mFrameCallback = new Choreographer.FrameCallback() {
        @Override
        public void doFrame(long frameTimeNanos) {
            mRebindRunnable.run();
        }
    };
} else {
    mFrameCallback = null;
    mUIThreadHandler = new Handler(Looper.myLooper());
}
  • 接著蜘犁,通過調(diào)用 mapBindings(...) 遍歷布局以獲得包含bound、includes止邮、ID Views的數(shù)組對(duì)象这橙,再依次賦給對(duì)應(yīng)View
final Object[] bindings = mapBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds);
this.mboundView0 = (android.widget.LinearLayout) bindings[0];
this.mboundView0.setTag(null);
  • 然后,調(diào)用 invalidateAll() -> requestRebind() -> ... -> mRebindRunnable.run() - 執(zhí)行 Runnable
// 用于動(dòng)態(tài)重新綁定 Views
private final Runnable mRebindRunnable = new Runnable() {
    @Override
    public void run() {
        synchronized (this) {
            mPendingRebind = false;
        }
        .....
        executePendingBindings();
    }
};
  • 最后导披,通過該Runnable會(huì)執(zhí)行到 executePendingBindings() -> ... -> executeBindings()屈扎,在這里會(huì)執(zhí)行綁定相關(guān)操作。
@Override
protected void executeBindings() {
    long dirtyFlags = 0;
    synchronized(this) {
        dirtyFlags = mDirtyFlags;   // mDirtyFlags 變量更新的標(biāo)志
        mDirtyFlags = 0;
    }
    .....
}

設(shè)置變量(數(shù)據(jù)對(duì)象)

普通 Java bean 對(duì)象

  • 首先撩匕,通過mDirtyFlags標(biāo)識(shí)變量(所有變量共用)
synchronized(this) {
    mDirtyFlags |= 0x1L;
}
  • 然后鹰晨,調(diào)用 notifyPropertyChanged(...) 來通知更新(若有回調(diào))
public void notifyPropertyChanged(int fieldId) {
    if (mCallbacks != null) {
        mCallbacks.notifyCallbacks(this, fieldId, null);
    }
}
  • 最后,調(diào)用 requestRebind() -> ... -> executeBindings() 再次執(zhí)行綁定操作止毕,將數(shù)據(jù)更新到Views上
@Override
protected void executeBindings() {
    long dirtyFlags = 0;
    synchronized(this) {
        dirtyFlags = mDirtyFlags;
        mDirtyFlags = 0;
    }
    .....
}

Observable 對(duì)象

  • 在設(shè)置變量時(shí)模蜡,會(huì)先調(diào)用 updateRegistration(..) 注冊(cè)一個(gè)Observable對(duì)象的監(jiān)聽
public void setContact(com.connorlin.databinding.model.ObservableContact contact) {
    updateRegistration(0, contact);
    this.mContact = contact;
    synchronized(this) {
        mDirtyFlags |= 0x1L;
    }
    notifyPropertyChanged(BR.contact);
    super.requestRebind();
}
  • 其他步驟同普通 Java bean 對(duì)象

ObservableFields 對(duì)象

  • 前期步驟同普通 Java Bean 對(duì)象

  • 與 Observable 對(duì)象不同的是,Observable對(duì)象的監(jiān)聽是在 executeBindings() 中注冊(cè)的

@Override
protected void executeBindings() {
    long dirtyFlags = 0;
    synchronized(this) {
        dirtyFlags = mDirtyFlags;
        mDirtyFlags = 0;
    }
    ...
    if ((dirtyFlags & 0xfL) != 0) {
        if ((dirtyFlags & 0xdL) != 0) {
            if (contact != null) {
                // read contact.mName
                mNameContact = contact.mName;
            }
            updateRegistration(0, mNameContact);

            if (mNameContact != null) {
                // read contact.mName.get()
                mNameContact1 = mNameContact.get();
            }
        }
        ...
    }
    ...
}

注冊(cè)O(shè)bservable對(duì)象監(jiān)聽

  • 入口 updateRegistration(0, contact)
protected boolean updateRegistration(int localFieldId, Observable observable) {
    return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
}

private boolean updateRegistration(int localFieldId, Object observable,
        CreateWeakListener listenerCreator) {
    ...
    // 確保不重復(fù)監(jiān)聽扁凛,先移除再添加觀察監(jiān)聽
    unregisterFrom(localFieldId);
    registerTo(localFieldId, observable, listenerCreator);
    return true;
}

protected void registerTo(int localFieldId, Object observable,
        CreateWeakListener listenerCreator) {
    if (observable == null) {
        return;
    }

    // 創(chuàng)建對(duì)象監(jiān)聽并存到mLocalFieldObservers中
    WeakListener listener = mLocalFieldObservers[localFieldId];
    if (listener == null) {
        // CREATE_PROPERTY_LISTENER -> create(...)
        listener = listenerCreator.create(this, localFieldId);
        mLocalFieldObservers[localFieldId] = listener;
    }

    // 將監(jiān)聽綁定到Observable對(duì)象上
    listener.setTarget(observable);
}

每個(gè)Observable對(duì)象都會(huì)添加一個(gè)觀察監(jiān)聽忍疾,保存在數(shù)組 mLocalFieldObservers 中,并以 localFieldId 索引令漂。

  • CREATE_PROPERTY_LISTENER 為何物膝昆?
private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
    @Override
    public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
        // 返回從WeakPropertyListener實(shí)例中獲取的監(jiān)聽器(WeakListener)
        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 WeakListener<Observable> getListener() {
        return mListener;
    }

    @Override
    public void addListener(Observable target) {
        // WeakPropertyListener 繼承于 Observable.OnPropertyChangedCallback,
        // 所以 this 其實(shí)就是 Observable對(duì)象的屬性監(jiān)聽器
        target.addOnPropertyChangedCallback(this);
    }

    ...
}

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

    ...

    public void setTarget(T object) {
        unregister();
        mTarget = object;
        if (mTarget != null) {
            // mObservable 是上面的 WeakPropertyListener對(duì)象
            // mTarget 是綁定到listener上得Observable對(duì)象
            mObservable.addListener(mTarget);
        }
    }

    ...
}

CREATE_PROPERTY_LISTENER 實(shí)際上只是一個(gè)接口實(shí)例叠必,注冊(cè)時(shí)會(huì)調(diào)用它的create()方法創(chuàng)建一個(gè)弱引用listener荚孵,它的作用是將listener綁定到Observable對(duì)象上,
綁定時(shí)纬朝,會(huì)調(diào)用 listener.setTarget(...) 將Observable對(duì)象傳給 WeakPropertyListener實(shí)例收叶,然后,WeakPropertyListener 會(huì)為 Observable對(duì)象添加OnPropertyChangedCallback共苛。

  • addOnPropertyChangedCallback實(shí)現(xiàn)

addOnPropertyChangedCallback 在 BaseObservable中實(shí)現(xiàn)判没,首先會(huì)實(shí)例化一個(gè)PropertyChangeRegistry對(duì)象,同時(shí)創(chuàng)建一個(gè)用來通知Observable對(duì)象重新綁定更新的回調(diào)CallbackRegistry.NotifierCallback隅茎。然后將 OnPropertyChangedCallback 添加到PropertyChangeRegistry的回調(diào)列表中

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

這樣澄峰,注冊(cè)O(shè)bservable對(duì)象的監(jiān)聽就完畢了。

更新(重新綁定)Observable對(duì)象

設(shè)置或更新Observable對(duì)象時(shí)都會(huì)調(diào)用notifyPropertyChanged()notifyChange()來通知更新辟犀,那到底是如何更新的呢俏竞?

  • 回調(diào)過程
public void notifyPropertyChanged(int fieldId) {
    // mCallbacks 是 PropertyChangeRegistry對(duì)象,在 addOnPropertyChangedCallback 時(shí)實(shí)例化
    // 如果注冊(cè)了Observable對(duì)象監(jiān)聽,那么mCallbacks不為null
    if (mCallbacks != null) {
        mCallbacks.notifyCallbacks(this, fieldId, null);
    }
}

// baseLibrary
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 是實(shí)例化PropertyChangeRegistry時(shí)創(chuàng)建的
            // mNotifier 即 CallbackRegistry.NotifierCallback
            this.mNotifier.onNotifyCallback(this.mCallbacks.get(i), sender, arg, arg2);
        }
        bitMask <<= 1;
    }
}

// PropertyChangeRegistry.NOTIFIER_CALLBACK
public void onNotifyCallback(Observable.OnPropertyChangedCallback callback, Observable sender,
        int arg, Void notUsed) {
    // callback 是為Observable對(duì)象添加的OnPropertyChangedCallback魂毁,即WeakPropertyListener
    callback.onPropertyChanged(sender, arg);
}

// WeakPropertyListener
public void onPropertyChanged(Observable sender, int propertyId) {
    // binder 即生成的Binding類對(duì)象
    ViewDataBinding binder = mListener.getBinder();
    ...
    binder.handleFieldChange(mListener.mLocalFieldId, sender, propertyId);
}

private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
    // onFieldChange 實(shí)現(xiàn)在生成的Binding類中
    boolean result = onFieldChange(mLocalFieldId, object, fieldId);
    if (result) {
        // 如果對(duì)象屬性變化玻佩,將重新綁定
        requestRebind();
    }
}

通過 notifyPropertyChanged 調(diào)用到 mNotifier 回調(diào), mNotifier 通知OnPropertyChangedCallback Observable對(duì)象屬性發(fā)生變化席楚,然后在onPropertyChanged中又轉(zhuǎn)給ViewDataBinding對(duì)象(生成的Binding類)處理咬崔。

  • 判斷是否需要重新綁定并執(zhí)行,在生成的Binding類中實(shí)現(xiàn)
// 生成的Binding類中得方法
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
    // 如果變量不是Observable類型或沒有添加 Bindable注解烦秩,就不會(huì)判斷垮斯,直接返回false
    switch (localFieldId) {
        case 0 :
            return onChangeContact((com.connorlin.databinding.model.ObservableContact) object, fieldId);
    }
    return false;
}

private boolean onChangeContact(com.connorlin.databinding.model.ObservableContact contact, int fieldId) {
    switch (fieldId) {
        case BR.name: {
            synchronized(this) {
                    mDirtyFlags |= 0x4L;// 通過mDirtyFlags判斷對(duì)象是否變化
            }
            return true;
        }
        ...
    }
    return false;
}

至此,更新過程完畢闻镶。

整個(gè)注冊(cè)與更新過程可以用一張流程圖來概括:

事件處理

事件處理的原理很簡(jiǎn)單甚脉,在生成Binding類中會(huì)實(shí)現(xiàn)View事件的監(jiān)聽,在構(gòu)造時(shí)實(shí)例化View的事件監(jiān)聽铆农,然后在綁定時(shí)將事件監(jiān)聽對(duì)象賦值給對(duì)應(yīng)View牺氨,這樣,點(diǎn)擊時(shí)就會(huì)觸發(fā)相應(yīng)的監(jiān)聽墩剖。

這里以 DataBindingDemoEventActivity部分為例:

  • 生成的Binding類并實(shí)現(xiàn)View的事件監(jiān)聽
public class ActivityEventBinding extends android.databinding.ViewDataBinding
    implements android.databinding.generated.callback.OnCheckedChangeListener.Listener,
        android.databinding.generated.callback.OnClickListener.Listener {
    // Checkbox check監(jiān)聽
    private final android.widget.CompoundButton.OnCheckedChangeListener mCallback3;
    private final android.view.View.OnClickListener mCallback2;
    private final android.view.View.OnClickListener mCallback1;
    // listeners
    private OnClickListenerImpl mAndroidViewViewOnCl;
    ...
    // Listener Stub Implementations
    public static class OnClickListenerImpl implements android.view.View.OnClickListener{
        private com.connorlin.databinding.handler.EventHandler value;
        public OnClickListenerImpl setValue(com.connorlin.databinding.handler.EventHandler value) {
            this.value = value;
            return value == null ? null : this;
        }
        @Override
        public void onClick(android.view.View arg0) {
            this.value.onClickFriend(arg0);
        }
    }
    ...
}
  • 實(shí)例化View的事件監(jiān)聽
public ActivityEventBinding(android.databinding.DataBindingComponent bindingComponent, View root) {
    super(bindingComponent, root, 0);
    ...
    // listeners
    mCallback3 = new android.databinding.generated.callback.OnCheckedChangeListener(this, 3);
    mCallback2 = new android.databinding.generated.callback.OnClickListener(this, 2);
    mCallback1 = new android.databinding.generated.callback.OnClickListener(this, 1);
    invalidateAll();
}
  • 在執(zhí)行綁定中綁定View事件監(jiān)聽
@Override
protected void executeBindings() {
    ...
    if ((dirtyFlags & 0x6L) != 0) {
        if (handler != null) {
            // read handler::onClickFriend
            androidViewViewOnCli = (((mAndroidViewViewOnCl == null)
                ? (mAndroidViewViewOnCl = new OnClickListenerImpl()) : mAndroidViewViewOnCl).setValue(handler));
        }
    }
    // batch finished
    if ((dirtyFlags & 0x6L) != 0) {
        this.mboundView1.setOnClickListener(androidViewViewOnCli);
    }
    if ((dirtyFlags & 0x4L) != 0) {
        this.mboundView2.setOnClickListener(mCallback1);
        this.mboundView3.setOnClickListener(mCallback2);
        android.databinding.adapters.CompoundButtonBindingAdapter.setListeners(
            this.mboundView4, mCallback3, (android.databinding.InverseBindingListener)null);
    }
}
  • 觸發(fā)事件并執(zhí)行

ViewStub

原理類似猴凹,只是利用 ViewStubProxy 來延遲綁定。

  • 使用layout中的ViewStub實(shí)例化一個(gè)ViewStubProxy對(duì)象賦給viewstub變量岭皂,并與Bingding關(guān)聯(lián)
public ActivityViewStubBinding(android.databinding.DataBindingComponent bindingComponent, View root) {
    super(bindingComponent, root, 0);
    final Object[] bindings = mapBindings(bindingComponent, root, 2, sIncludes, sViewsWithIds);
    ...
    this.viewStub = new android.databinding.ViewStubProxy((android.view.ViewStub) bindings[1]);
    this.viewStub.setContainingBinding(this);
    ...
}
  • 實(shí)例化ViewStubProxy的同時(shí)會(huì)注冊(cè)inflate監(jiān)聽
private OnInflateListener mProxyListener = new OnInflateListener() {
    @Override
    public void onInflate(ViewStub stub, View inflated) {
        mRoot = inflated;
        mViewDataBinding = DataBindingUtil.bind(mContainingBinding.mBindingComponent,
                inflated, stub.getLayoutResource());
        mViewStub = null;

        if (mOnInflateListener != null) {
            mOnInflateListener.onInflate(stub, inflated);
            mOnInflateListener = null;
        }
        mContainingBinding.invalidateAll();
        mContainingBinding.forceExecuteBindings();
    }
};

public ViewStubProxy(ViewStub viewStub) {
    mViewStub = viewStub;
    mViewStub.setOnInflateListener(mProxyListener);
}
  • inflate ViewStub
if (!mActivityViewStubBinding.viewStub.isInflated()) {
    mActivityViewStubBinding.viewStub.getViewStub().inflate();
}

當(dāng)ViewStub infate時(shí)郊霎,執(zhí)行mProxyListener,其中會(huì)生成ViewStub的Binding爷绘,并強(qiáng)制執(zhí)行主Binding重綁

  • 綁定ViewStub
@Override
protected void executeBindings() {
    long dirtyFlags = 0;
    synchronized(this) {
        dirtyFlags = mDirtyFlags;
        mDirtyFlags = 0;
    }
    // batch finished
    if (viewStub.getBinding() != null) {
        viewStub.getBinding().executePendingBindings();
    }
}

這樣书劝,ViewStub綁定就結(jié)束了。

本篇完土至,敬請(qǐng)期待下篇...


我的簡(jiǎn)書賬號(hào)是 ConnorLin购对,歡迎關(guān)注!

我的簡(jiǎn)書專題是 Android開發(fā)技術(shù)分享陶因,歡迎關(guān)注骡苞!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市楷扬,隨后出現(xiàn)的幾起案子解幽,更是在濱河造成了極大的恐慌,老刑警劉巖烘苹,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件躲株,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡镣衡,警方通過查閱死者的電腦和手機(jī)徘溢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門吞琐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人然爆,你說我怎么就攤上這事∈蛲迹” “怎么了曾雕?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)助被。 經(jīng)常有香客問我剖张,道長(zhǎng),這世上最難降的妖魔是什么揩环? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任搔弄,我火速辦了婚禮,結(jié)果婚禮上丰滑,老公的妹妹穿的比我還像新娘顾犹。我一直安慰自己,他們只是感情好褒墨,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布炫刷。 她就那樣靜靜地躺著,像睡著了一般郁妈。 火紅的嫁衣襯著肌膚如雪浑玛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天噩咪,我揣著相機(jī)與錄音顾彰,去河邊找鬼。 笑死胃碾,一個(gè)胖子當(dāng)著我的面吹牛涨享,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播书在,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼灰伟,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了儒旬?” 一聲冷哼從身側(cè)響起栏账,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎栈源,沒想到半個(gè)月后挡爵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡甚垦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年茶鹃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了涣雕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡闭翩,死狀恐怖挣郭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情疗韵,我是刑警寧澤兑障,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站蕉汪,受9級(jí)特大地震影響流译,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜者疤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一福澡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧驹马,春花似錦革砸、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至寇蚊,卻和暖如春笔时,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背仗岸。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國打工允耿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人扒怖。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓较锡,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親盗痒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蚂蕴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容