During the inflate layout, the Activity generates the Binding through DataBindingUtil. From the code point of View, it traverses the contentView to get the View array object, and then generates the corresponding Binding class through the data Binding library, including Views, variables, listeners, etc. The generated classes are located in build/intermediates/classes/debug /... package... / databinding/xxx Java How to generate is not discussed here.
Binding process
- First, the callback or Handler will be instantiated in the parent class (ViewDataBinding) for subsequent 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, traverse the layout by calling mapBindings(...) to obtain the array objects containing bound, includes and ID Views, and then assign them to the corresponding View in turn
final Object[] bindings = mapBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds); this.mboundView0 = (Android.widget.LinearLayout) bindings[0]; this.mboundView0.setTag(null);
- Then, call invalidateAll () - > requestRebind () - >... > mRebindRunnable.. Run () – execute Runnable
// Used to dynamically rebind Views private final Runnable mRebindRunnable = new Runnable() { @Override public void run() { synchronized (this) { mPendingRebind = false; } ..... executePendingBindings(); } };
- Finally, the Runnable will execute to executependingbindings() - >... - > executebindings(), where binding related operations will be performed.
@Override protected void executeBindings() { long dirtyFlags = 0; synchronized(this) { dirtyFlags = mDirtyFlags; // Flag of mDirtyFlags variable update mDirtyFlags = 0; } ..... }
Setting variables (data objects)
Ordinary Java bean object
- First, identify variables through mDirtyFlags (all variables share)
synchronized(this) { mDirtyFlags |= 0x1L; }
- Then, call notifyPropertyChanged(...) to notify the update (if there is a callback).
public void notifyPropertyChanged(int fieldId) { if (mCallbacks != null) { mCallbacks.notifyCallbacks(this, fieldId, null); } }
- Finally, call requestRebind () - > - > executeBindings () to perform the binding operation again, and update the data to Views.
@Override protected void executeBindings() { long dirtyFlags = 0; synchronized(this) { dirtyFlags = mDirtyFlags; mDirtyFlags = 0; } ..... }
Observable object
- When setting variables, updateRegistration(...) will be called to register the listener of an Observable object
public void setContact(com.connorlin.databinding.model.ObservableContact contact) { updateRegistration(0, contact); this.mContact = contact; synchronized(this) { mDirtyFlags |= 0x1L; } notifyPropertyChanged(BR.contact); super.requestRebind(); }
- Other steps are the same as normal Java bean object
ObservableFields object
- The previous steps are the same as those of the general Java Bean object
- Unlike the Observable object, the listener of the Observable object is 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(); } } ... } ... }
Register Observable object listener
- 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) { ... // Ensure that the monitoring is not repeated. Remove the monitoring first and then add the observation monitoring unregisterFrom(localFieldId); registerTo(localFieldId, observable, listenerCreator); return true; } protected void registerTo(int localFieldId, Object observable, CreateWeakListener listenerCreator) { if (observable == null) { return; } // Create an object to mLocalFieldObservers WeakListener listener = mLocalFieldObservers[localFieldId]; if (listener == null) { // CREATE_PROPERTY_LISTENER -> create(...) listener = listenerCreator.create(this, localFieldId); mLocalFieldObservers[localFieldId] = listener; } // Bind listener to Observable object listener.setTarget(observable); }
Each Observable object will add an observation listener, which is saved in the array mLocalFieldObservers and indexed by localFieldId.
- CREATE_ PROPERTY_ What is listener?
private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() { @Override public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) { // Returns the listener (WeakListener) obtained from the WeakPropertyListener instance 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 inherits from observable OnPropertyChangedCallback, // So this is actually the property listener of the Observable object 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 is the above WeakPropertyListener object // mTarget is the Observable object bound to the listener mObservable.addListener(mTarget); } } ... }
CREATE_PROPERTY_LISTENER is actually just an interface instance. When registering, it will call its create() method to create a weak reference listener. Its function is to bind the listener to the Observable object. When binding, it will call listener SETTARGET (...) passes the Observable object to the WeakPropertyListener instance, and then the WeakPropertyListener adds OnPropertyChangedCallback to the Observable object.
- addOnPropertyChangedCallback implementation
addOnPropertyChangedCallback is implemented in BaseObservable. First, a PropertyChangeRegistry object will be instantiated, and a callback callbackregistry will be created to notify the Observable object to rebind the update NotifierCallback. Then add OnPropertyChangedCallback to the callback list of PropertyChangeRegistry
@Override public synchronized void addOnPropertyChangedCallback(OnPropertyChangedCallback callback) { if (mCallbacks == null) { mCallbacks = new PropertyChangeRegistry(); } mCallbacks.add(callback); }
In this way, the listening of the registered Observable object is completed.
Update (rebind) Observable objects
When setting or updating Observable objects, notifyPropertyChanged() or notifyChange() will be called to notify the update. How is it updated?
- call-back procedure
public void notifyPropertyChanged(int fieldId) { // mCallbacks is a PropertyChangeRegistry object, which is instantiated when addOnPropertyChangedCallback // If Observable object listening is registered, mCallbacks is 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) { // The mNotifier is created when the PropertyChangeRegistry is instantiated // mNotifier is 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 is the OnPropertyChangedCallback added for the Observable object, that is, the WeakPropertyListener callback.onPropertyChanged(sender, arg); } // WeakPropertyListener public void onPropertyChanged(Observable sender, int propertyId) { // The binder is the generated Binding class object ViewDataBinding binder = mListener.getBinder(); ... binder.handleFieldChange(mListener.mLocalFieldId, sender, propertyId); } private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) { // onFieldChange is implemented in the generated Binding class boolean result = onFieldChange(mLocalFieldId, object, fieldId); if (result) { // If the object properties change, they are rebound requestRebind(); } }
Call the notifyPropertyChanged to the mNotifier callback. The mNotifier notifies onpropertychangedcallback that the property of the observable object has changed, and then transfers it to the ViewDataBinding object (generated Binding class) in onPropertyChanged for processing.
- Determine whether you need to rebind and execute it, and implement it in the generated Binding class
// Methods in the generated Binding class protected boolean onFieldChange(int localFieldId, Object object, int fieldId) { // If the variable is not of Observable type or the Bindable annotation is not added, it will not be judged and will directly 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;// Judge whether the object changes through mDirtyFlags } return true; } ... } return false; }
This completes the update process.
The whole registration and update process can be summarized in a flowchart:
event processing
The principle of event handling is very simple. The Binding class will implement the monitoring of View events. The event monitoring of View will be instantiated during construction, and then the event monitoring object will be assigned to the corresponding View during Binding. In this way, the corresponding monitoring will be triggered when clicking.
Here with DataBindingDemo Take the EventActivity section in as an example:
- Generate the Binding class and implement the event listening of View
public class ActivityEventBinding extends Android.databinding.ViewDataBinding implements Android.databinding.generated.callback.OnCheckedChangeListener.Listener, Android.databinding.generated.callback.OnClickListener.Listener { // Checkbox check listening 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); } } ... }
- Event listening of instantiated 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 View event listener in 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); } }
- Trigger event and execute
ViewStub
The principle is similar, except that ViewStubProxy is used to delay binding.
- Use the viewstub in the layout to instantiate a viewstub proxy object, assign it to the viewstub variable, and associate it with Bingding
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); ... }
- Instantiating ViewStubProxy will register the inflate listener at the same time
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(); }
When ViewStub infeed, execute mProxyListener, where the Binding of ViewStub will be generated and the main Binding rebinding will be enforced
- Bind ViewStub
@Override protected void executeBindings() { long dirtyFlags = 0; synchronized(this) { dirtyFlags = mDirtyFlags; mDirtyFlags = 0; } // batch finished if (viewStub.getBinding() != null) { viewStub.getBinding().executePendingBindings(); } }
This completes the ViewStub binding.