從DataBinding源碼理解Databinding工作過程

原文地址: 從DataBinding源碼理解Databinding工作過程

摘要

前幾天突然就開始看Databinding寥袭,之前在上家公司,最后一個項目中使用過這個技術(shù)关霸,相似的技術(shù)我接觸過得還有Butter Knife传黄,但是顯然沒有DataBinding功能強大,而且還是Google官方推出的队寇,在功能和兼容性上膘掰,都要更勝一籌。

之前項目中佳遣,只是學習了怎么使用DataBinding這個技術(shù)识埋,但是并沒有深入理解,這樣一個很蛋疼的問題就是:項目如果能正常運行零渐,那么沒有問題窒舟,但是一旦出現(xiàn)問題,你可能連編譯器里面的錯誤都看不明白诵盼,更何況去解決問題惠豺。所以我最近幾天重新學習了一下DataBinding银还。而且是從基礎(chǔ)的使用方法重新學習的。(沒辦法洁墙,都忘了蛹疯。)但是今天咱們不介紹具體使用,想要了解使用方法的可以直接去看官方文檔热监,或者看我最近翻譯的幾篇Blog捺弦。(我其實還是比較推薦直接去看官方文檔,簡單直接好用)

下面我們就進入今天的主題:Databinding到底是怎么實現(xiàn)雙向數(shù)據(jù)綁定的孝扛?

DataBinding生成的代碼介紹

項目在buildhou羹呵,如果使用了Databinding, 那么會由DataBinding compiler生成ActivityMainBindingImpl.java,ActivityMainBinding.java,BR.java,DataBinderMapperImpl.java等代碼疗琉,咱們主要看這四個類冈欢,數(shù)據(jù)和方法的綁定都是在這里。

ActivityMainBinding和ActivityMainBindingImpl

位置: app/build/generated/data_binding_base_class_source_out/debug/dataBindingGenBaseClassesDebug/out/com/example/databindingpractise/databinding/ActivityMainBinding.java

app/build/generated/source/apt/debug/com/example/databindingpractise/databinding/ActivityMainBindingImpl.java

在我們的MainActivity中盈简,我們使用:DataBindingUtil.setContentView(this, R.layout.activity_main);方法獲得一個ViewDataBinding對象凑耻,這是編譯器通過layout和綁定viewModel生成的,用于將數(shù)據(jù)和方法綁定到layout中

public abstract class ActivityMainBinding extends ViewDataBinding {
  @NonNull
  public final TextView textView;

  @NonNull
  public final UpAndDownChoiceLayoutBinding upanddown;

  @Bindable
  protected MainPresenter mPresenter;

  @Bindable
  protected MainViewModel mViewModel;

  @Bindable
  protected UpDownChoicePresenter mUpDownPresenter;

  @Bindable
  protected UpDownChoiceViewModel mUpDownviewModel;

  ....
}
public class ActivityMainBindingImpl extends ActivityMainBinding  {

    ...
    // views
    @NonNull
    private final android.widget.LinearLayout mboundView0;
    @NonNull
    private final android.widget.Button mboundView2;
    @NonNull
    private final android.widget.CheckBox mboundView3;
    
   
    
  ...
}

可以看到:ActivityMainBindingImpl是繼承自ActivityMainBinding

  1. ActivityMainBinding
    • 持有含有id的View的引用
    • 持有綁定的類的引用
  2. ActivityMainBindingImpl
    • 持有沒有id的View的引用
    • 具體實現(xiàn)了綁定

BR

位置:app/build/generated/source/apt/debug/com/example/databindingpractise/BR.java

public class BR {
  public static final int _all = 0;

  public static final int presenter = 1;

  public static final int upDownviewModel = 2;

  public static final int viewModel = 3;

  ...
}

BR文件儲存了VM的id柠贤, 功能與R文件類似

DataBinderMapperImpl

public class DataBinderMapperImpl extends DataBinderMapper {
  ....

  @Override
  public ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) {
    int localizedLayoutId = INTERNAL_LAYOUT_ID_LOOKUP.get(layoutId);
    if(localizedLayoutId > 0) {
      final Object tag = view.getTag();
      if(tag == null) {
        throw new RuntimeException("view must have a tag");
      }
      switch(localizedLayoutId) {
        case  LAYOUT_ACTIVITYMAIN: {
          if ("layout/activity_main_0".equals(tag)) {
            return new ActivityMainBindingImpl(component, view);
          }
          throw new IllegalArgumentException("The tag for activity_main is invalid. Received: " + tag);
        }
      }
    }
    return null;
  }
  ...
  
}

這個類主要是提供了從布局文件layoutId到ViewDataBinding類對象的映射香浩,主要是用于在加載Layout返回對應的ViewDataBinding對象。

初始化

在這個模塊臼勉,我們通過查看ViewDataBinding源碼邻吭,來了解DataBinding是如何將activity_main.layout上的View實例引用保存在對應的ActivityMainBindingActivityMainBindingImpl上的。

我們看一下在MainActivity中執(zhí)行:

dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);

內(nèi)部進行了什么操作宴霸。

進入DataBindingUtil.setContentView

public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity,
            int layoutId) {
        return setContentView(activity, layoutId, sDefaultComponent);
}

public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity,
            int layoutId, @Nullable DataBindingComponent bindingComponent) {
        activity.setContentView(layoutId);
        View decorView = activity.getWindow().getDecorView();
        ViewGroup contentView = (ViewGroup) decorView.findViewById(android.R.id.content);
        return bindToAddedViews(bindingComponent, contentView, 0, layoutId);
}

可以看到囱晴,其實內(nèi)部還是調(diào)用了我們原來使用的activity.setContentView(layoutId);填充布局,并拿到了content布局瓢谢,并傳入bindToAddedViews()方法:

private static <T extends ViewDataBinding> T bindToAddedViews(DataBindingComponent component,ViewGroup parent, int startChildren, int layoutId) {
        final int endChildren = parent.getChildCount();
        final int childrenAdded = endChildren - startChildren;
        if (childrenAdded == 1) {
            final View childView = parent.getChildAt(endChildren - 1);
            return bind(component, childView, layoutId);
        } else {
            final View[] children = new View[childrenAdded];
            for (int i = 0; i < childrenAdded; i++) {
                children[i] = parent.getChildAt(i + startChildren);
            }
            return bind(component, children, layoutId);
        }
}

判斷布局是否有多個子布局畸写,如果有則遍歷存入View數(shù)組,最后調(diào)用不同參數(shù)的bind方法

static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View root,int layoutId) {
        return (T) sMapper.getDataBinder(bindingComponent, root, layoutId);
}

最后調(diào)用DataBinderMapperImpl中的getDataBinder()方法氓扛, 獲得對應Layout的VIewDataBinding類實例

接下來我們看看枯芬,是如何將Layout中的View賦值到ViewDataBinding中的引用:

public ActivityMainBindingImpl(@Nullable android.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
        this(bindingComponent, root, mapBindings(bindingComponent, root, 5, sIncludes, sViewsWithIds));
}
    private ActivityMainBindingImpl(android.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
        super(bindingComponent, root, 2
            , (android.widget.TextView) bindings[1]
            , (com.example.databindingpractise.databinding.UpAndDownChoiceLayoutBinding) bindings[4]
            );
        this.mboundView0 = (android.widget.LinearLayout) bindings[0];
        this.mboundView0.setTag(null);
        this.mboundView2 = (android.widget.Button) bindings[2];
        this.mboundView2.setTag(null);
        this.mboundView3 = (android.widget.CheckBox) bindings[3];
        this.mboundView3.setTag(null);
        this.textView.setTag(null);
        setRootTag(root);
        // listeners
        invalidateAll();
}

當實例化ActivityMainBindingImpl時,我們主要看mapBindings()方法采郎,這個方法將會把layout中的view 存入數(shù)組中bindings[]

protected static Object[] mapBindings(DataBindingComponent bindingComponent, View root,
            int numBindings, IncludedLayouts includes, SparseIntArray viewsWithIds) {
        Object[] bindings = new Object[numBindings];
        mapBindings(bindingComponent, root, bindings, includes, viewsWithIds, true);
        return bindings;
}

然后調(diào)用同名方法千所,由于這個方法比較長,我們分成三部分來看:

  1. 從View的tag中獲取緩存蒜埋,防止多次初始化

    
    private static void mapBindings(DataBindingComponent   bindingComponent, View view, Object[] bindings, IncludedLayouts includes, SparseIntArray viewsWithIds, boolean isRoot) {
        ...
            final int indexInIncludes;
            final ViewDataBinding existingBinding = getBinding(view);
            if (existingBinding != null) {
                return;
            }
        ...
        
    }
        
    
    static ViewDataBinding getBinding(View v) {
            if (v != null) {
                return (ViewDataBinding) v.getTag(R.id.dataBinding);
            }
            return null;
    }
    
  2. 將view儲存在bindings數(shù)組內(nèi)淫痰,分為三種情況:

    1. 根布局,tag以layout開頭理茎;
    2. 設(shè)置@{}的黑界,tag以binding開頭
    3. 設(shè)置了id的view
    private static void mapBindings(DataBindingComponent bindingComponent, View view, Object[] bindings, IncludedLayouts includes, SparseIntArray viewsWithIds,boolean isRoot) {
            ...
            Object objTag = view.getTag();
            final String tag = (objTag instanceof String) ? (String) objTag : null;
            boolean isBound = false;
            if (isRoot && tag != null && tag.startsWith("layout")) {
                final int underscoreIndex = tag.lastIndexOf('_');
                if (underscoreIndex > 0 && isNumeric(tag, underscoreIndex + 1)) {
                    final int index = parseTagInt(tag, underscoreIndex + 1);
                    if (bindings[index] == null) {
                        bindings[index] = view;
                    }
                    indexInIncludes = includes == null ? -1 : index;
                    isBound = true;
                } else {
                    indexInIncludes = -1;
                }
            } else if (tag != null && tag.startsWith(BINDING_TAG_PREFIX)) {
                int tagIndex = parseTagInt(tag, BINDING_NUMBER_START);
                if (bindings[tagIndex] == null) {
                    bindings[tagIndex] = view;
                }
                isBound = true;
                indexInIncludes = includes == null ? -1 : tagIndex;
            } else {
                // Not a bound view
                indexInIncludes = -1;
            }
            if (!isBound) {
                final int id = view.getId();
                if (id > 0) {
                    int index;
                    if (viewsWithIds != null && (index = viewsWithIds.get(id, -1)) >= 0 &&
                            bindings[index] == null) {
                        bindings[index] = view;
                    }
                }
            }
        ....
    }
    
  3. 第三部分判斷根布局是不是ViewGroup管嬉,如果是則遍歷根布局,并判斷子View是不是include的朗鸠,如果是的話蚯撩,則使用DataBindingUtil.bind進行遞歸;如果不是include烛占,則直接使用mapBindings進行遞歸胎挎。

通過這三個步驟,遞歸得到最后的bindings[]忆家,如果設(shè)置了id的犹菇,就將view變量設(shè)置為public,這樣就避免了findViewById的代碼芽卿。

這種方式從性能上比findViewById高效揭芍,因為databinding只需要遍歷一次view樹,而findViewById多次調(diào)用會遍歷多次卸例。

private ActivityMainBindingImpl(android.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
        super(bindingComponent, root, 2
            , (android.widget.TextView) bindings[1]
            , (com.example.databindingpractise.databinding.UpAndDownChoiceLayoutBinding) bindings[4]
            );
        this.mboundView0 = (android.widget.LinearLayout) bindings[0];
        this.mboundView0.setTag(null);
        this.mboundView2 = (android.widget.Button) bindings[2];
        this.mboundView2.setTag(null);
        this.mboundView3 = (android.widget.CheckBox) bindings[3];
        this.mboundView3.setTag(null);
        this.textView.setTag(null);
        setRootTag(root);
        // listeners
        invalidateAll();
    }

最后通過setRootTag()方法將View緩存起來称杨。

調(diào)用invalidateAll()方法,更新布局筷转,這個方法我們在初始化綁定中講姑原。

這樣我們ActivityMainBindingActivityMainBindingImpl中View引用都對應上View對象了。

初始化綁定

初始化綁定使用如下代碼:

dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
dataBinding.setVariable(BR.viewModel, mainViewModel);
//
dataBinding.setViewModel(mainViewMOdel);

上面兩個綁定方式其實本質(zhì)上一致的呜舒,最后調(diào)用的方法都是 setViewModel()

@Override
public boolean setVariable(int variableId, @Nullable Object variable)  {
        boolean variableSet = true;
        if (BR.viewModel == variableId) {
            setViewModel(
            (com.example.databindingpractise.viewModel.MainViewModel) variable);
        }
        else {
            variableSet = false;
        }
            return variableSet;
}

我們主要看一下setViewModel()方法:

public void setViewModel(@Nullable com.example.databindingpractise.viewModel.MainViewModel ViewModel) {
        this.mViewModel = ViewModel;
        synchronized(this) {
            mDirtyFlags |= 0x20L;
        }
        notifyPropertyChanged(BR.viewModel);
        super.requestRebind();
}

mDirtyFlags用于表示哪個屬性發(fā)生變化锭汛,notifyPropertyChanged(BR.viewModel),顧名思義袭蝗,是發(fā)出viewModel數(shù)據(jù)變化的通知唤殴。看看requestRebind是干什么的:

protected void requestRebind() {
        if (mContainingBinding != null) {
            mContainingBinding.requestRebind();
        } else {
            synchronized (this) {
                if (mPendingRebind) {
                    return;
                }
                mPendingRebind = true;
            }
            if (mLifecycleOwner != null) {
                Lifecycle.State state = mLifecycleOwner.getLifecycle().getCurrentState();
                if (!state.isAtLeast(Lifecycle.State.STARTED)) {
                    return; // wait until lifecycle owner is started
                }
            }
            if (USE_CHOREOGRAPHER) {
                mChoreographer.postFrameCallback(mFrameCallback);
            } else {
                mUIThreadHandler.post(mRebindRunnable);
            }
        }
}

mContainingBinding當include子布局的時候呻袭,該屬性不為空眨八;mPendingRebind屬性表示腺兴,是否正在綁定左电,如果是就直接返回;mLigecyeleOwner對象可用于追蹤View的生命周期页响,如果不為空篓足,且不為start狀態(tài),直接return闰蚕;最后然后根據(jù)api版本做了點不同的處理栈拖,16及以上的,會往mChoreographer發(fā)一mFrameCallback没陡;否則直接往UI線程發(fā)一個mRebindRunnable涩哟。其實這里倆個分支的結(jié)果基本一致索赏,mChoreographer會在界面刷新時執(zhí)行mRebindRunnableChoreographer是api16后引入的用于解決UI卡頓的贴彼,當收到VSYNC(定時中斷)時潜腻,在doFrame里去執(zhí)行相應的操作。

我們再看看mRebindRUnnable里面做了什么:

    /**
     * Runnable executed on animation heartbeat to rebind the dirty Views.
     */
    private final Runnable mRebindRunnable = new Runnable() {
        @Override
        public void run() {
            synchronized (this) {
                mPendingRebind = false;
            }
            processReferenceQueue();

            if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
                // Nested so that we don't get a lint warning in IntelliJ
                if (!mRoot.isAttachedToWindow()) {
                    // Don't execute the pending bindings until the View
                    // is attached again.
                    mRoot.removeOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
                    mRoot.addOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
                    return;
                }
            }
            executePendingBindings();
        }
    };

mPendingRebind置為false器仗;

processReferenceQueue()融涣,移除ViewDataBindings中存的weakListener

/**
* Polls sReferenceQueue to remove listeners on ViewDataBindings that have been collected.
*/
private static void processReferenceQueue() {
        Reference<? extends ViewDataBinding> ref;
        while ((ref = sReferenceQueue.poll()) != null) {
            if (ref instanceof WeakListener) {
                WeakListener listener = (WeakListener) ref;
                listener.unregister();
            }
        }
 }

如果api19及以上時,判斷rootView是否attach到window上精钮,如果沒有的話威鹿,則對這個attach的狀態(tài)進行監(jiān)聽。最終都會執(zhí)行executePendingBindings():

/**
* Evaluates the pending bindings, updating any Views that have expressions bound to
* modified variables. This <b>must</b> be run on the UI thread.
*/
public void executePendingBindings() {
    if (mContainingBinding == null) {
        executeBindingsInternal();
    } else {
        mContainingBinding.executePendingBindings();
    }
}

繼而調(diào)用executeBindingsInternal()方法轨香。

private void executeBindingsInternal() {
        if (mIsExecutingPendingBindings) {
            requestRebind();
            return;
        }
        if (!hasPendingBindings()) {
            return;
        }
        mIsExecutingPendingBindings = true;
        mRebindHalted = false;
        if (mRebindCallbacks != null) {
            mRebindCallbacks.notifyCallbacks(this, REBIND, null);

            // The onRebindListeners will change mPendingHalted
            if (mRebindHalted) {
                mRebindCallbacks.notifyCallbacks(this, HALTED, null);
            }
        }
        if (!mRebindHalted) {
            executeBindings();
            if (mRebindCallbacks != null) {
                mRebindCallbacks.notifyCallbacks(this, REBOUND, null);
            }
        }
        mIsExecutingPendingBindings = false;
}

hasPendingBindings方法返回是否有數(shù)據(jù)需要綁定忽你,如果當前沒有需要需要綁定的數(shù)據(jù),則返回不處理臂容。接下來通知所有的RebindCallback檀夹,RebindCallback可以通過ActivityMainBinding.addOnRebindCallback設(shè)置。RebindCallback里可以把mRebindHalted置為true策橘,以終止后面的executeBindings()方法炸渡。如果被終止了,同樣HALTED事件也會通知給所有的RebindCallback丽已。

executeBindings()是一個抽象的方法蚌堵,具體實現(xiàn)在編譯時生成的ActivityMainBindingImpl里。

由于代碼很多沛婴,我們也分成兩部分:

  1. 將View中的值與viewModel中的值做綁定吼畏,并設(shè)置監(jiān)聽值的變化和View變化

    protected void executeBindings() {
            long dirtyFlags = 0;
            synchronized(this) {
                dirtyFlags = mDirtyFlags;
                mDirtyFlags = 0;
            }
            android.databinding.ObservableField<java.lang.String> viewModelName = null;
            
            java.lang.String viewModelNameGet = null;
            com.example.databindingpractise.viewModel.MainViewModel viewModel = mViewModel;
    
                   
            if ((dirtyFlags & 0x62L) != 0) {
    
                    if (viewModel != null) {
                        // read viewModel.name
                        viewModelName = viewModel.name;
                    }
                    updateRegistration(1, viewModelName);
    
                    if (viewModelName != null) {
                        // read viewModel.name.get()
                        viewModelNameGet = viewModelName.get();
                    }
            }
           
            if ((dirtyFlags & 0x62L) != 0) {
                // api target 1
    
                android.databinding.adapters.TextViewBindingAdapter.setText(this.textView, viewModelNameGet);
            }
            if ((dirtyFlags & 0x40L) != 0) {
                // api target 1
    
                android.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.textView, (android.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged)null, (android.databinding.adapters.TextViewBindingAdapter.OnTextChanged)null, (android.databinding.adapters.TextViewBindingAdapter.AfterTextChanged)null, textViewandroidTextAttrChanged);
            }
            
        }
    
  2. 將Layout中的事件監(jiān)聽動作與對應的viewModel方法綁定

protected void executeBindings() {
    if ((dirtyFlags & 0x44L) != 0) {
                if (presenter != null) {
                    // read presenter::onCheckedChanged
                    presenterOnCheckedChangedAndroidWidgetCompoundButtonOnCheckedChangeListener = (((mPresenterOnCheckedChangedAndroidWidgetCompoundButtonOnCheckedChangeListener == null) ? (mPresenterOnCheckedChangedAndroidWidgetCompoundButtonOnCheckedChangeListener = new OnCheckedChangeListenerImpl()) : mPresenterOnCheckedChangedAndroidWidgetCompoundButtonOnCheckedChangeListener).setValue(presenter));
                    // read presenter::changeName
                    presenterChangeNameAndroidViewViewOnClickListener = (((mPresenterChangeNameAndroidViewViewOnClickListener == null) ? (mPresenterChangeNameAndroidViewViewOnClickListener = new OnClickListenerImpl()) : mPresenterChangeNameAndroidViewViewOnClickListener).setValue(presenter));
                }
        }
     // batch finished
     if ((dirtyFlags & 0x44L) != 0) {
            // api target 1            this.mboundView2.setOnClickListener(presenterChangeNameAndroidViewViewOnClickListener);
            android.databinding.adapters.CompoundButtonBindingAdapter.setListeners(this.mboundView3, (android.widget.CompoundButton.OnCheckedChangeListener)presenterOnCheckedChangedAndroidWidgetCompoundButtonOnCheckedChangeListener, (android.databinding.InverseBindingListener)null);
        }
}

事件與方法的綁定這里就不說了,上述代碼基本已經(jīng)很清晰的展示了嘁灯,我們主要分析一下ViewModel上值的變化泻蚊,他是通過updateRegistration(1, viewModelName);方法實現(xiàn)的:

protected boolean updateRegistration(int localFieldId, Observable observable) {
        return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
}

這里面?zhèn)魅胪椒ǖ牡谌齻€參數(shù)需要我們注意一下:

private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
        @Override
        public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
            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) {
            target.addOnPropertyChangedCallback(this);
        }

        @Override
        public void removeListener(Observable target) {
            target.removeOnPropertyChangedCallback(this);
        }

        @Override
        public void setLifecycleOwner(LifecycleOwner lifecycleOwner) {
        }

        @Override
        public void onPropertyChanged(Observable sender, int propertyId) {
            ViewDataBinding binder = mListener.getBinder();
            if (binder == null) {
                return;
            }
            Observable obj = mListener.getTarget();
            if (obj != sender) {
                return; // notification from the wrong object?
            }
            binder.handleFieldChange(mListener.mLocalFieldId, sender, propertyId);
        }
 }
 
private static class WeakListener<T> extends WeakReference<ViewDataBinding> {
    private final ObservableReference<T> mObservable;
    protected final int mLocalFieldId;
    private T mTarget;

    public WeakListener(ViewDataBinding binder, int localFieldId,
            ObservableReference<T> observable) {
        super(binder, sReferenceQueue);
        mLocalFieldId = localFieldId;
        mObservable = observable;
    }

    // …… 
}

從上面知道CREATE_PROPERTY_LISTENER是一個CreateWeakListener對象骚亿,CreateWeakListener.create()能得到WeakPropertyListener膊夹,WeakPropertyListener內(nèi)有變量WeakListener毡代,WeakListener持有ViewDataBinding以及Observable(即VM)物喷。

我們接著上面看看updateRegistration里面的事情:

private boolean updateRegistration(int localFieldId, Object observable,
        CreateWeakListener listenerCreator) {
    if (observable == null) {
        return unregisterFrom(localFieldId);
    }
    WeakListener listener = mLocalFieldObservers[localFieldId];
    if (listener == null) {
        registerTo(localFieldId, observable, listenerCreator);
        return true;
    }
    if (listener.getTarget() == observable) {
        return false;//nothing to do, same object
    }
    unregisterFrom(localFieldId);
    registerTo(localFieldId, observable, listenerCreator);
    return true;
}

先從mLocalFieldObservers[]取localFieldId對應的WeakListener泥技,如果為null的話骑脱,就調(diào)用registerTo()進行注冊痴柔;如果不為空裳涛,而且與之前注冊過的不一致的話诀拭,則重新注冊迁筛。那registerTo里面如何進行注冊?

protected void registerTo(int localFieldId, Object observable,
        CreateWeakListener listenerCreator) {
    if (observable == null) {
        return;
    }
    WeakListener listener = mLocalFieldObservers[localFieldId];
    if (listener == null) {
        listener = listenerCreator.create(this, localFieldId);
        mLocalFieldObservers[localFieldId] = listener;
    }
    listener.setTarget(observable);
}

registerTo把CreateWeakListener存儲在mLocalFieldObservers里面耕挨。

這樣一來V和VM的聯(lián)系就通過ViewDatabinding建立起來了细卧。V內(nèi)有ViewDatabinding尉桩,而ViewDatabinding里持有各個View的引用。ViewDataBinding有VM的變量贪庙,而VM內(nèi)的PropertyChangeRegistry監(jiān)聽實則為WeakPropertyListener魄健,WeakListener能獲取到ViewDatabinding

VM變化如何通知View

我們知道插勤,如果要達到VM變化時自動綁定到View上沽瘦,有下面?zhèn)z種方式:

  1. 繼承自BaseObservable,在getter上增加@Bindable注解农尖,在setter里增加代碼notifyPropertyChanged(BR.xxx)析恋。
  2. 無需繼承,需要將屬性替換為Observable類盛卡,例如ObservableInt助隧、ObservableField等。
    /**
     * Notifies listeners that all properties of this instance have changed.
     */
    public void notifyChange() {
        synchronized (this) {
            if (mCallbacks == null) {
                return;
            }
        }
        mCallbacks.notifyCallbacks(this, 0, null);
    }

    /**
     * Notifies listeners that a specific property has changed. The getter for the property
     * that changes should be marked with {@link Bindable} to generate a field in
     * <code>BR</code> to be used as <code>fieldId</code>.
     *
     * @param fieldId The generated BR id for the Bindable field.
     */
    public void notifyPropertyChanged(int fieldId) {
        synchronized (this) {
            if (mCallbacks == null) {
                return;
            }
        }
        mCallbacks.notifyCallbacks(this, fieldId, null);
    }

不同的是滑沧,一個回調(diào)的方法是notifyChange()并村, 一個回調(diào)的方法是notifyPropertyChanged(),不過最后都是通過mCallbacks.notifyCallbacks()返回滓技。

然后哩牍,這個監(jiān)聽是在綁定的時候做的設(shè)定:

當調(diào)用listener.setTarget(observable)時,其實是設(shè)置了viewModel的監(jiān)聽事件:

@Override
public void addListener(Observable target) {
   target.addOnPropertyChangedCallback(this);
}

V的變化如何同步到VM

這個相對就簡單一下了令漂,是直接對對應的View設(shè)置setTextWatcher方法

if ((dirtyFlags & 0x40L) != 0) {
            // api target 1

            android.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.textView, (android.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged)null, (android.databinding.adapters.TextViewBindingAdapter.OnTextChanged)null, (android.databinding.adapters.TextViewBindingAdapter.AfterTextChanged)null, textViewandroidTextAttrChanged);
        

總結(jié)

這篇文章主要跟根據(jù)源碼來熟悉DataBinding是如何雙向綁定數(shù)據(jù)和方法在Layout上的膝昆,在弄清楚這一套邏輯之后,個人覺得DataBinding感覺是通過一種語法規(guī)則叠必,將綁定數(shù)據(jù)和方法自動化荚孵,省卻了很多需要人為的重復的工作,例如findViewById纬朝,setOnClickListener等等收叶,讓我們能有更多的精力專注于邏輯控制。值得一提的是共苛,使用Databinding很容易構(gòu)造出MVVM模式的代碼判没。

參考文獻:DataBinding源碼解析

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市俄讹,隨后出現(xiàn)的幾起案子哆致,更是在濱河造成了極大的恐慌,老刑警劉巖患膛,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異耻蛇,居然都是意外死亡踪蹬,警方通過查閱死者的電腦和手機胞此,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來跃捣,“玉大人漱牵,你說我怎么就攤上這事【纹幔” “怎么了酣胀?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長娶聘。 經(jīng)常有香客問我闻镶,道長,這世上最難降的妖魔是什么丸升? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任铆农,我火速辦了婚禮,結(jié)果婚禮上狡耻,老公的妹妹穿的比我還像新娘墩剖。我一直安慰自己,他們只是感情好夷狰,可當我...
    茶點故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布岭皂。 她就那樣靜靜地躺著,像睡著了一般沼头。 火紅的嫁衣襯著肌膚如雪蒲障。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天瘫证,我揣著相機與錄音揉阎,去河邊找鬼。 笑死背捌,一個胖子當著我的面吹牛毙籽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播毡庆,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼坑赡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了么抗?” 一聲冷哼從身側(cè)響起毅否,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蝇刀,沒想到半個月后螟加,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年捆探,在試婚紗的時候發(fā)現(xiàn)自己被綠了然爆。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,015評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡黍图,死狀恐怖曾雕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情助被,我是刑警寧澤剖张,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站揩环,受9級特大地震影響搔弄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜检盼,卻給世界環(huán)境...
    茶點故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一肯污、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吨枉,春花似錦蹦渣、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至圃庭,卻和暖如春锄奢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背剧腻。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工拘央, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人书在。 一個月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓灰伟,卻偏偏與公主長得像,于是被迫代替她去往敵國和親儒旬。 傳聞我的和親對象是個殘疾皇子栏账,可洞房花燭夜當晚...
    茶點故事閱讀 44,969評論 2 355

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