The Activity generates the Binding with the Inflate Layout through DataBindingUtil. As you can see from the code, you iterate through the contentView to get the View array object and then generate the corresponding Binding class through the data Binding library. Contains Views, variables, listeners, etc. Generated classes in the build/intermediates/classes/debug /… Package… /databinding/xxx.Java, the details of how to generate here will not go into depth.

The binding process

  • First, a callback or Handler is instantiated in the parent class (ViewDataBinding) for later binding operations;

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

  • Next, by calling mapBindings(…) Iterate over the layout

    ] (www.zhihu.com/search?q=%E… Array object of Views, and then assigned to the corresponding View

    final Object[] bindings = mapBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds); this.mboundView0 = (Android.widget.LinearLayout) bindings[0]; this.mboundView0.setTag(null);

  • Then, call invalidateAll() -> requestRebind() ->… -> mrebindrunable.run () – Run Runnable

    Views private final Runnable mRebindRunnable = new Runnable() {@override public void run() {synchronized (this) { mPendingRebind = false; }… executePendingBindings(); }};

  • Finally, this Runnable is executed to executePendingBindings() -> bindings (). -> executeBindings(), where bindings are performed.

    @Override protected void executeBindings() { long dirtyFlags = 0; synchronized(this) { dirtyFlags = mDirtyFlags; MDirtyFlags = 0; // mDirtyFlags = 0; }… }

Setting variables (data objects)

Plain Java Bean objects

  • First, variables are identified by mDirtyFlags[

    ] (www.zhihu.com/search?q=%E…

    synchronized(this) { mDirtyFlags |= 0x1L; }

  • Then, call notifyPropertyChanged(…) To notify updates (if there are callbacks)

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

  • Finally, call requestRebind() ->… -> executeBindings() performs bindings again, updating data to Views

    @Override protected void executeBindings() { long dirtyFlags = 0; synchronized(this) { dirtyFlags = mDirtyFlags; mDirtyFlags = 0; }… }

Observables object

  • When setting variables, updateRegistration(..) is called first. Register an Observable listener

    public void setContact(com.connorlin.databinding.model.ObservableContact contact) { updateRegistration(0, contact); this.mContact = contact; synchronized(this) { mDirtyFlags |= 0x1L; } notifyPropertyChanged(BR.contact); super.requestRebind(); }

  • The other steps are the same as normal Java Bean objects

ObservableFields object

  • The early steps are the same as normal Java Bean objects

  • Unlike Observables, observables’ listeners are registered in executeBindings()

    @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(); }}... }...Copy the code

    }

Register an Observable to listen on

  • Entry 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) { … UnregisterFrom (localFieldId) unregisterFrom(localFieldId); registerTo(localFieldId, observable, listenerCreator); return true; }

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

    WeakListener Listener = mLocalFieldObservers[localFieldId]; WeakListener = mLocalFieldObservers[localFieldId]; if (listener == null) { // CREATE_PROPERTY_LISTENER -> create(...) listener = listenerCreator.create(this, localFieldId); mLocalFieldObservers[localFieldId] = listener; } // Bind listeners to An Observable listener.settarget (Observable);Copy the code

    }

Each Observable adds an observation listener to it, stored in the array mLocalFieldObservers and indexed as localFieldId.

  • What is CREATE_PROPERTY_LISTENER?

    private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() { @Override public WeakListener create(ViewDataBinding viewDataBinding, Int localFieldId) {// Return the listener obtained from the WeakPropertyListener instance (WeakListener) return new WeakPropertyListener(viewDataBinding, localFieldId).getListener(); }}

    private static class WeakPropertyListener extends Observable.OnPropertyChangedCallback implements ObservableReference { final WeakListener 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 inherits from Observables. OnPropertyChangedCallback, / / so this attributes of the object is actually observables listener target. AddOnPropertyChangedCallback (this); }...Copy the code

    }

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

    . public void setTarget(T object) { unregister(); mTarget = object; if (mTarget ! // mObservable is the WeakPropertyListener object above. // mTarget is the Observable bound to the Listener mObservable.addListener(mTarget); }}...Copy the code

    }

CREATE_PROPERTY_LISTENER is just an interface instance that calls its create() method during registration to create a weak reference listener that binds the listener to an Observable. Invokes the listener. SetTarget (…). Pass WeakPropertyListener observables object instance, then WeakPropertyListener will add OnPropertyChangedCallback objects to observables.

  • AddOnPropertyChangedCallback implementation

AddOnPropertyChangedCallback in BaseObservable implementation, first will instantiate a PropertyChangeRegistry object, At the same time create a used to inform observables object rebind the update callback CallbackRegistry. The NotifierCallback. Then add to the list of the callback PropertyChangeRegistry OnPropertyChangedCallback

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

So, register an Observable

Object listening is done.

Update (rebind) the Observable

NotifyPropertyChanged () or notifyChange() is called when an Observable is set or updated. How does that change?

  • The callback process

    Public void notifyPropertyChanged(int fieldId) {// mCallbacks is the PropertyChangeRegistry object, Registered in addOnPropertyChangedCallback instantiation / / if the object of observables listening, then mCallbacks not 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 is created when instantiating PropertyChangeRegistry CallbackRegistry.NotifierCallback this.mNotifier.onNotifyCallback(this.mCallbacks.get(i), sender, arg, arg2); } bitMask <<= 1; }}

    // PropertyChangeRegistry.NOTIFIER_CALLBACK public void onNotifyCallback(Observable.OnPropertyChangedCallback callback, Observables sender, int arg, Void notUsed) {/ / callback for observables OnPropertyChangedCallback object is added, Namely WeakPropertyListener callback. OnPropertyChanged (sender, arg); }

    // WeakPropertyListener public void onPropertyChanged(Observable sender, // Bind ViewDataBinding binder = mListener.getBinder(); . binder.handleFieldChange(mListener.mLocalFieldId, sender, propertyId); }

    private void handleFieldChange(int mLocalFieldId, Object object, Boolean result = onFieldChange(mLocalFieldId, object, fieldId); If (result) {// If object attributes change, requestRebind() will be rebound; }}

By notifyPropertyChanged calls to mNotifier callback, notify OnPropertyChangedCallback mNotifier observables object properties change, This is then passed on to the ViewDataBinding object (the generated Binding class) in onPropertyChanged.

  • Determine if you need to rebind and implement it in the generated Binding class

    Protected Boolean onFieldChange(int localFieldId, Object Object, LocalFieldId) {// If the variable is not an Observable or Bindable annotation, return 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; } return true; }… } return false; }

At this point, the update process is complete.

The whole registration and update process can be summarized in a flow chart:

The event processing

The View event listener is instantiated at construction time, and then assigned to the corresponding View at Binding time, so that when clicked, the corresponding listener will be triggered.

Use the EventActivity section of DataBindingDemo as an example:

  • Generates a Binding class and implements event listening for the View

    public class ActivityEventBinding extends Android.databinding.ViewDataBinding implements Android.databinding.generated.callback.OnCheckedChangeListener.Listener, Android. Databinding. Generated. Callback. An OnClickListener. Listener {/ / the Checkbox monitored private final check 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); }}… }

  • Instantiate event listeners for a View

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

  • Bind the View event listener in the execution binding

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

  • Fires the event and executes

ViewStub

The principle is similar, but ViewStubProxy is used to delay binding.

  • Instantiate a ViewStubProxy object from Layout and assign it to the ViewStub variable

    ] (www.zhihu.com/search?q=vi…

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

  • The Instantiation of ViewStubProxy registers the Inflate listener

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

    };

    public ViewStubProxy(ViewStub viewStub) { mViewStub = viewStub; mViewStub.setOnInflateListener(mProxyListener); }

  • inflate ViewStub

    if (! mActivityViewStubBinding.viewStub.isInflated()) { mActivityViewStubBinding.viewStub.getViewStub().inflate(); }

When ViewStub infate, execute mProxyListener, which generates a Binding for the ViewStub and forces a master Binding rebinding

  • Binding ViewStub

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

At this point, the ViewStub binding ends.