AndroidのDataBindingの原理紹介
136451 ワード
Activityはinflate layoutの場合、DataBindingUtilによってバインディングを生成し、コードから見てcontentViewを遍歴してView配列オブジェクトを得、その後、データバインディングlibraryによって対応するBindingクラスを生成し、Views、変数、listenersなどを含む.生成クラスはbuild/intermediates/classes/debug/...package.../databinding/xxxにある.Javaの下で、具体的にどのように生成するかは、ここでは詳しくは説明しません.
バインドプロセスまず、親クラス(ViewDataBinding)でコールバックまたはHandlerがインスタンス化され、その後のバインド操作に使用されます. 次に、mapBindings(…)を呼び出してレイアウトを巡回し、bound、includes、ID Viewsを含む配列オブジェクトを取得し、対応するView に順次付与する次に、invalidateAll()->requestRebind()->...->mRebindRunnableを呼び出す.run()–Runnable の実行最後に、RunnableによってexecutePendingBindings()->...->executeBindings()に実行され、ここでバインド関連操作が実行されます.
変数の設定(データ・オブジェクト)
通常のJava beanオブジェクトまず、mDirtyFlagsによって変数(全変数共通) を識別するそして、notifyPropertyChanged(…)を呼び出して更新を通知する(コールバックがある場合) 最後にrequestRebind()->...->executeBindings()を呼び出してバインディング操作を再実行し、データをViewsに更新
Observableオブジェクト変数を設定すると、updateRegistrationが呼び出されます(.)Observableオブジェクトを登録する傍受 その他の手順は、通常のJava beanオブジェクト と同じです.
ObservableFieldsオブジェクト前期ステップ同普通Java Beanオブジェクト Observableオブジェクトとは異なり、Observableオブジェクトの傍受はexecuteBindings()に登録されている
Observableオブジェクトリスニングの登録入口updateRegistration(0,contact):
各Observableオブジェクトには、配列mLocalFieldObserversに保存され、localFieldIdインデックスとしてオブザーバリスニングが追加されます. CREATE_PROPERTY_LISTENERとは何ですか?
CREATE_PROPERTY_LISTENERは実際にはインタフェースのインスタンスにすぎず、登録時にcreate()メソッドを呼び出して弱参照listenerを作成し、listenerをObservableオブジェクトにバインドし、バインド時にlistenerを呼び出す役割を果たす.setTarget(…)は、ObservableオブジェクトをWeakPropertyListenerインスタンスに渡し、WeakPropertyListenerはObservableオブジェクトにOnPropertyChangedCallbackを追加します.addOnPropertyChangedCallback実装 addOnPropertyChangedCallbackはBaseObservableで実装され、まずPropertyChangeRegistryオブジェクトをインスタンス化するとともに、Observableオブジェクトに更新の再バインドを通知するコールバックCallbackRegistryを作成する.NotifierCallback.次に、OnPropertyChangedCallbackをPropertyChangeRegistryのコールバックリストに追加します.
これで、Observableオブジェクトを登録する傍受が完了します.
Observableオブジェクトの更新(再バインド)
Observableオブジェクトを設定または更新すると、notifyPropertyChanged()またはnotifyChange()が呼び出されて更新が通知されますが、どのように更新されますか?コールバックプロセス
mNotifyPropertyChangedからmNotifierコールバックを呼び出すと、mNotiferはOnPropertyChangedCallback Observableオブジェクトのプロパティが変化したことを通知し、onPropertyChangedでViewDataBindingオブジェクト(生成されたBindingクラス)に処理を転送します.再バインドして実行する必要があるかどうかを判断し、生成されたBindingクラスで を実現する
これで更新プロセスが完了します.
登録と更新プロセス全体をフローチャートで要約できます.
イベント処理
イベント処理の原理は簡単で、Bindingクラスを生成するとViewイベントのリスニングが実現され、構築時にViewのイベントリスニングがインスタンス化され、バインディング時にイベントリスニングオブジェクトが対応するViewに付与され、クリックすると対応するリスニングがトリガーされます.
ここでは、DataBindingDemoのEventActivity部分を例に挙げます.生成されたBindingクラスであり、Viewのイベント傍受を実現 インスタンス化Viewのイベントリスニング バインディングの実行におけるViewイベント傍受のバインディング イベントをトリガーして実行 ViewStub
原理は類似しているが,バインディングを遅延させるためにViewStubProxyを利用するだけである.layoutのViewStubを使用して、viewstub変数に割り当てられたViewStubProxyオブジェクトをインスタンス化し、Bingdingに関連付け ViewStubProxyをインスタンス化するとinflateリスニング が登録されます. inflate ViewStub
ViewStub infateの場合、mProxyListenerが実行され、ViewStubのBindingが生成され、プライマリBinding再バインドが強制的に実行されます.バインディングViewStub
これで、ViewStubバインドは終了です.
バインドプロセス
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());
}
final Object[] bindings = mapBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds);
this.mboundView0 = (Android.widget.LinearLayout) bindings[0];
this.mboundView0.setTag(null);
// Views
private final Runnable mRebindRunnable = new Runnable() {
@Override
public void run() {
synchronized (this) {
mPendingRebind = false;
}
.....
executePendingBindings();
}
};
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags; // mDirtyFlags
mDirtyFlags = 0;
}
.....
}
変数の設定(データ・オブジェクト)
通常のJava beanオブジェクト
synchronized(this) {
mDirtyFlags |= 0x1L;
}
public void notifyPropertyChanged(int fieldId) {
if (mCallbacks != null) {
mCallbacks.notifyCallbacks(this, fieldId, null);
}
}
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
.....
}
Observableオブジェクト
public void setContact(com.connorlin.databinding.model.ObservableContact contact) {
updateRegistration(0, contact);
this.mContact = contact;
synchronized(this) {
mDirtyFlags |= 0x1L;
}
notifyPropertyChanged(BR.contact);
super.requestRebind();
}
ObservableFieldsオブジェクト
@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();
}
}
...
}
...
}
Observableオブジェクトリスニングの登録
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);
registerTo(localFieldId, observable, listenerCreator);
return true;
}
protected void registerTo(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
if (observable == null) {
return;
}
// mLocalFieldObservers
WeakListener listener = mLocalFieldObservers[localFieldId];
if (listener == null) {
// CREATE_PROPERTY_LISTENER -> create(...)
listener = listenerCreator.create(this, localFieldId);
mLocalFieldObservers[localFieldId] = listener;
}
// Observable
listener.setTarget(observable);
}
各Observableオブジェクトには、配列mLocalFieldObserversに保存され、localFieldIdインデックスとしてオブザーバリスニングが追加されます.
private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
@Override
public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
// WeakPropertyListener (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 Observable
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
// mTarget listener Observable
mObservable.addListener(mTarget);
}
}
...
}
CREATE_PROPERTY_LISTENERは実際にはインタフェースのインスタンスにすぎず、登録時にcreate()メソッドを呼び出して弱参照listenerを作成し、listenerをObservableオブジェクトにバインドし、バインド時にlistenerを呼び出す役割を果たす.setTarget(…)は、ObservableオブジェクトをWeakPropertyListenerインスタンスに渡し、WeakPropertyListenerはObservableオブジェクトにOnPropertyChangedCallbackを追加します.
@Override
public synchronized void addOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
if (mCallbacks == null) {
mCallbacks = new PropertyChangeRegistry();
}
mCallbacks.add(callback);
}
これで、Observableオブジェクトを登録する傍受が完了します.
Observableオブジェクトの更新(再バインド)
Observableオブジェクトを設定または更新すると、notifyPropertyChanged()またはnotifyChange()が呼び出されて更新が通知されますが、どのように更新されますか?
public void notifyPropertyChanged(int fieldId) {
// mCallbacks PropertyChangeRegistry , addOnPropertyChangedCallback
// Observable , 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 PropertyChangeRegistry
// 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 OnPropertyChangedCallback, WeakPropertyListener
callback.onPropertyChanged(sender, arg);
}
// WeakPropertyListener
public void onPropertyChanged(Observable sender, int propertyId) {
// binder Binding
ViewDataBinding binder = mListener.getBinder();
...
binder.handleFieldChange(mListener.mLocalFieldId, sender, propertyId);
}
private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
// onFieldChange Binding
boolean result = onFieldChange(mLocalFieldId, object, fieldId);
if (result) {
// ,
requestRebind();
}
}
mNotifyPropertyChangedからmNotifierコールバックを呼び出すと、mNotiferはOnPropertyChangedCallback Observableオブジェクトのプロパティが変化したことを通知し、onPropertyChangedでViewDataBindingオブジェクト(生成されたBindingクラス)に処理を転送します.
// Binding
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
// Observable Bindable , , 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
}
return true;
}
...
}
return false;
}
これで更新プロセスが完了します.
登録と更新プロセス全体をフローチャートで要約できます.
イベント処理
イベント処理の原理は簡単で、Bindingクラスを生成するとViewイベントのリスニングが実現され、構築時にViewのイベントリスニングがインスタンス化され、バインディング時にイベントリスニングオブジェクトが対応するViewに付与され、クリックすると対応するリスニングがトリガーされます.
ここでは、DataBindingDemoのEventActivity部分を例に挙げます.
public class ActivityEventBinding extends Android.databinding.ViewDataBinding
implements Android.databinding.generated.callback.OnCheckedChangeListener.Listener,
Android.databinding.generated.callback.OnClickListener.Listener {
// Checkbox check
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);
}
}
...
}
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();
}
@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);
}
}
原理は類似しているが,バインディングを遅延させるためにViewStubProxyを利用するだけである.
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);
...
}
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);
}
if (!mActivityViewStubBinding.viewStub.isInflated()) {
mActivityViewStubBinding.viewStub.getViewStub().inflate();
}
ViewStub infateの場合、mProxyListenerが実行され、ViewStubのBindingが生成され、プライマリBinding再バインドが強制的に実行されます.
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
// batch finished
if (viewStub.getBinding() != null) {
viewStub.getBinding().executePendingBindings();
}
}
これで、ViewStubバインドは終了です.