摘要
前幾天突然就開始看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
的
-
ActivityMainBinding
- 持有含有
id
的View的引用 - 持有綁定的類的引用
- 持有含有
-
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實例引用保存在對應的ActivityMainBinding
和ActivityMainBindingImpl
上的。
我們看一下在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)用同名方法千所,由于這個方法比較長,我們分成三部分來看:
-
從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; }
-
將view儲存在
bindings
數(shù)組內(nèi)淫痰,分為三種情況:- 根布局,tag以layout開頭理茎;
- 設(shè)置@{}的黑界,tag以binding開頭
- 設(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; } } } .... }
第三部分判斷根布局是不是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()
方法,更新布局筷转,這個方法我們在初始化綁定中講姑原。
這樣我們ActivityMainBinding
和ActivityMainBindingImpl
中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í)行mRebindRunnable
,Choreographer
是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
里。
由于代碼很多沛婴,我們也分成兩部分:
-
將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); } }
將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種方式:
- 繼承自
BaseObservable
,在getter上增加@Bindable注解农尖,在setter里增加代碼notifyPropertyChanged(BR.xxx)
析恋。 - 無需繼承,需要將屬性替換為
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源碼解析