AndroidのDataBindingの原理紹介

136451 ワード

Activityはinflate layoutの場合、DataBindingUtilによってバインディングを生成し、コードから見てcontentViewを遍歴してView配列オブジェクトを得、その後、データバインディングlibraryによって対応するBindingクラスを生成し、Views、変数、listenersなどを含む.生成クラスはbuild/intermediates/classes/debug/...package.../databinding/xxxにある.Javaの下で、具体的にどのように生成するかは、ここでは詳しくは説明しません.
バインドプロセス
  • まず、親クラス(ViewDataBinding)でコールバックまたは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());
    }
  • 次に、mapBindings(…)を呼び出してレイアウトを巡回し、bound、includes、ID Viewsを含む配列オブジェクトを取得し、対応するView
  • に順次付与する
    final Object[] bindings = mapBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds);
    this.mboundView0 = (Android.widget.LinearLayout) bindings[0];
    this.mboundView0.setTag(null);
  • 次に、invalidateAll()->requestRebind()->...->mRebindRunnableを呼び出す.run()–Runnable
  • の実行
    //          Views
    private final Runnable mRebindRunnable = new Runnable() {
       
    @Override
       
    public void run() {
           
    synchronized (this) {
                mPendingRebind
    = false;
           
    }
           
    .....
            executePendingBindings
    ();
       
    }
    };
  • 最後に、RunnableによってexecutePendingBindings()->...->executeBindings()に実行され、ここでバインド関連操作が実行されます.
  • @Override
    protected void executeBindings() {
       
    long dirtyFlags = 0;
       
    synchronized(this) {
            dirtyFlags
    = mDirtyFlags;   // mDirtyFlags
            mDirtyFlags
    = 0;
       
    }
       
    .....
    }

    変数の設定(データ・オブジェクト)
    通常のJava beanオブジェクト
  • まず、mDirtyFlagsによって変数(全変数共通)
  • を識別する
    synchronized(this) {
        mDirtyFlags
    |= 0x1L;
    }
  • そして、notifyPropertyChanged(…)を呼び出して更新を通知する(コールバックがある場合)
  • public void notifyPropertyChanged(int fieldId) {
       
    if (mCallbacks != null) {
            mCallbacks
    .notifyCallbacks(this, fieldId, null);
       
    }
    }
  • 最後にrequestRebind()->...->executeBindings()を呼び出してバインディング操作を再実行し、データをViewsに更新
  • @Override
    protected void executeBindings() {
       
    long dirtyFlags = 0;
       
    synchronized(this) {
            dirtyFlags
    = mDirtyFlags;
            mDirtyFlags
    = 0;
       
    }
       
    .....
    }

    Observableオブジェクト
  • 変数を設定すると、updateRegistrationが呼び出されます(.)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();
    }
  • その他の手順は、通常のJava beanオブジェクト
  • と同じです.
    ObservableFieldsオブジェクト
  • 前期ステップ同普通Java Beanオブジェクト
  • Observableオブジェクトとは異なり、Observableオブジェクトの傍受は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();
               
    }
           
    }
           
    ...
       
    }
       
    ...
    }

    Observableオブジェクトリスニングの登録
  • 入口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);
        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インデックスとしてオブザーバリスニングが追加されます.
  • CREATE_PROPERTY_LISTENERとは何ですか?
  • 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を追加します.
  • addOnPropertyChangedCallback実装
  • addOnPropertyChangedCallbackはBaseObservableで実装され、まずPropertyChangeRegistryオブジェクトをインスタンス化するとともに、Observableオブジェクトに更新の再バインドを通知するコールバックCallbackRegistryを作成する.NotifierCallback.次に、OnPropertyChangedCallbackをPropertyChangeRegistryのコールバックリストに追加します.
    @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クラスで
  • を実現する
    //    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部分を例に挙げます.
  • 生成されたBindingクラスであり、Viewのイベント傍受を実現
  • 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);
           
    }
       
    }
       
    ...
    }
  • インスタンス化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
    ();
    }
  • バインディングの実行におけるViewイベント傍受のバインディング
  • @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);
       
    }
    }
  • イベントをトリガーして実行
  • ViewStub
    原理は類似しているが,バインディングを遅延させるためにViewStubProxyを利用するだけである.
  • layoutのViewStubを使用して、viewstub変数に割り当てられたViewStubProxyオブジェクトをインスタンス化し、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);
       
    ...
    }
  • ViewStubProxyをインスタンス化するとinflateリスニング
  • が登録されます.
    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();
    }

    ViewStub infateの場合、mProxyListenerが実行され、ViewStubのBindingが生成され、プライマリ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バインドは終了です.