DataBinding是Google發(fā)布的支持庫莽鸭,它可以實(shí)現(xiàn)UI組件及數(shù)據(jù)源的雙向綁定金蜀。使用DataBinding可以輕松實(shí)現(xiàn)MVVM模式刷后,當(dāng)數(shù)據(jù)發(fā)生變化時會體現(xiàn)在View界面上的畴,反過來界面內(nèi)容變化也會同步更新到ViewModel中的數(shù)據(jù)源。同時尝胆,在Google推出的Android Jetpack組件中丧裁,也將DataBinding放在了Architecture類別之中。
一含衔、DataBinding生成代碼
啟用DataBinding的方法是在對應(yīng)Model的build.gradle文件里加入以下代碼煎娇,同步后就能引入對 DataBinding 的支持。
android {
dataBinding {
enabled = true
}
}
DataBinding的布局文件使用了layout標(biāo)簽作為根節(jié)點(diǎn)贪染,其中包含了data標(biāo)簽與view標(biāo)簽view標(biāo)簽缓呛。打開xml布局文件,根據(jù)DataBinding需要的布局規(guī)則進(jìn)行編寫杭隙,實(shí)例如下:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewmodel"
type="com.lxbnjupt.databindingdemo.UserViewModel"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewmodel.name.get()}" />
</LinearLayout>
</layout>
Databinding在編譯時會生成代碼哟绊,利用的技術(shù)是Apt(annotation-processing-tool)。在我們按照規(guī)則編寫完xml之后寺渗,就會生成相應(yīng)的java文件匿情,文件名為xml文件名加上Binding后綴兰迫,按照上述實(shí)例會生成ActivityMainBinding.java文件券坞,其生成的路徑為/app/build/generated/source/apt/debug/com/lxbnjupt/databindingdemo/databinding/ActivityMainBinding万牺。
package com.lxbnjupt.databindingdemo.databinding;
import com.lxbnjupt.databindingdemo.R;
import com.lxbnjupt.databindingdemo.BR;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.View;
@SuppressWarnings("unchecked")
public class ActivityMainBinding extends android.databinding.ViewDataBinding {
@Nullable
private static final android.databinding.ViewDataBinding.IncludedLayouts sIncludes;
@Nullable
private static final android.util.SparseIntArray sViewsWithIds;
static {
sIncludes = null;
sViewsWithIds = null;
}
// views
@NonNull
private final android.widget.LinearLayout mboundView0;
@NonNull
public final android.widget.TextView tvName;
// variables
@Nullable
private com.lxbnjupt.databindingdemo.UserViewModel mViewmodel;
// values
// listeners
// Inverse Binding Event Handlers
public ActivityMainBinding(@NonNull android.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
super(bindingComponent, root, 1);
final Object[] bindings = mapBindings(bindingComponent, root, 2, sIncludes, sViewsWithIds);
this.mboundView0 = (android.widget.LinearLayout) bindings[0];
this.mboundView0.setTag(null);
this.tvName = (android.widget.TextView) bindings[1];
this.tvName.setTag(null);
setRootTag(root);
// listeners
invalidateAll();
}
@Override
public void invalidateAll() {
synchronized(this) {
mDirtyFlags = 0x4L;
}
requestRebind();
}
@Override
public boolean hasPendingBindings() {
synchronized(this) {
if (mDirtyFlags != 0) {
return true;
}
}
return false;
}
@Override
public boolean setVariable(int variableId, @Nullable Object variable) {
boolean variableSet = true;
if (BR.viewmodel == variableId) {
setViewmodel((com.lxbnjupt.databindingdemo.UserViewModel) variable);
}
else {
variableSet = false;
}
return variableSet;
}
public void setViewmodel(@Nullable com.lxbnjupt.databindingdemo.UserViewModel Viewmodel) {
this.mViewmodel = Viewmodel;
synchronized(this) {
mDirtyFlags |= 0x2L;
}
notifyPropertyChanged(BR.viewmodel);
super.requestRebind();
}
@Nullable
public com.lxbnjupt.databindingdemo.UserViewModel getViewmodel() {
return mViewmodel;
}
@Override
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
switch (localFieldId) {
case 0 :
return onChangeViewmodelName((android.databinding.ObservableField<java.lang.String>) object, fieldId);
}
return false;
}
private boolean onChangeViewmodelName(android.databinding.ObservableField<java.lang.String> ViewmodelName, int fieldId) {
if (fieldId == BR._all) {
synchronized(this) {
mDirtyFlags |= 0x1L;
}
return true;
}
return false;
}
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
com.lxbnjupt.databindingdemo.UserViewModel viewmodel = mViewmodel;
java.lang.String viewmodelNameGet = null;
android.databinding.ObservableField<java.lang.String> viewmodelName = null;
if ((dirtyFlags & 0x7L) != 0) {
if (viewmodel != null) {
// read viewmodel.name
viewmodelName = viewmodel.name;
}
updateRegistration(0, viewmodelName);
if (viewmodelName != null) {
// read viewmodel.name.get()
viewmodelNameGet = viewmodelName.get();
}
}
// batch finished
if ((dirtyFlags & 0x7L) != 0) {
// api target 1
android.databinding.adapters.TextViewBindingAdapter.setText(this.tvName, viewmodelNameGet);
}
}
// Listener Stub Implementations
// callback impls
// dirty flag
private long mDirtyFlags = 0xffffffffffffffffL;
@NonNull
public static ActivityMainBinding inflate(@NonNull android.view.LayoutInflater inflater, @Nullable android.view.ViewGroup root, boolean attachToRoot) {
return inflate(inflater, root, attachToRoot, android.databinding.DataBindingUtil.getDefaultComponent());
}
@NonNull
public static ActivityMainBinding inflate(@NonNull android.view.LayoutInflater inflater, @Nullable android.view.ViewGroup root, boolean attachToRoot, @Nullable android.databinding.DataBindingComponent bindingComponent) {
return android.databinding.DataBindingUtil.<ActivityMainBinding>inflate(inflater, com.lxbnjupt.databindingdemo.R.layout.activity_main, root, attachToRoot, bindingComponent);
}
@NonNull
public static ActivityMainBinding inflate(@NonNull android.view.LayoutInflater inflater) {
return inflate(inflater, android.databinding.DataBindingUtil.getDefaultComponent());
}
@NonNull
public static ActivityMainBinding inflate(@NonNull android.view.LayoutInflater inflater, @Nullable android.databinding.DataBindingComponent bindingComponent) {
return bind(inflater.inflate(com.lxbnjupt.databindingdemo.R.layout.activity_main, null, false), bindingComponent);
}
@NonNull
public static ActivityMainBinding bind(@NonNull android.view.View view) {
return bind(view, android.databinding.DataBindingUtil.getDefaultComponent());
}
@NonNull
public static ActivityMainBinding bind(@NonNull android.view.View view, @Nullable android.databinding.DataBindingComponent bindingComponent) {
if (!"layout/activity_main_0".equals(view.getTag())) {
throw new RuntimeException("view tag isn't correct on view:" + view.getTag());
}
return new ActivityMainBinding(bindingComponent, view);
}
/* flag mapping
flag 0 (0x1L): viewmodel.name
flag 1 (0x2L): viewmodel
flag 2 (0x3L): null
flag mapping end*/
//end
}
二、DataBinding如何避免findViewById
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
}
}
在初始化時,我們利用DataBindingUtil.setContentView()方法來建立layout與ViewDataBinding之間的關(guān)系收奔,我們具體看一下該方法的實(shí)現(xiàn)。
public static <T extends ViewDataBinding> T setContentView(Activity activity, int layoutId) {
return setContentView(activity, layoutId, sDefaultComponent);
}
public static <T extends ViewDataBinding> T setContentView(Activity activity, int layoutId,
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);
}
由源碼可知肩民,該方法先是調(diào)用了activity.setContentView(layoutId)蔽豺,這個與我們一般在Activity中的使用方式完全一致。接著獲取Activity的DecorView棘利,然后再將id為android.R.id.content的view傳入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);
}
}
static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View root,
int layoutId) {
return (T) sMapper.getDataBinder(bindingComponent, root, layoutId);
}
獲取到layout中的根布局,并且調(diào)用bind()方法善玫。而bind()方法內(nèi)調(diào)用DataBinderMapper的getDataBinder方法水援。DataBinderMapper也是編譯時生成的類,主要是建立layout與ViewDataBinding之間的映射茅郎,其路徑為/app/build/generated/source/apt/debug/android/databinding/DataBinderMapper.java蜗元。
package android.databinding;
import com.lxbnjupt.databindingdemo.BR;
class DataBinderMapper {
final static int TARGET_MIN_SDK = 18;
public DataBinderMapper() {
}
public android.databinding.ViewDataBinding getDataBinder(android.databinding.DataBindingComponent bindingComponent, android.view.View view, int layoutId) {
switch(layoutId) {
case com.lxbnjupt.databindingdemo.R.layout.activity_main:
return com.lxbnjupt.databindingdemo.databinding.ActivityMainBinding.bind(view, bindingComponent);
}
return null;
}
android.databinding.ViewDataBinding getDataBinder(android.databinding.DataBindingComponent bindingComponent, android.view.View[] views, int layoutId) {
switch(layoutId) {
}
return null;
}
... ...
}
由源碼可知,根據(jù)layoutId會去調(diào)用對應(yīng)ViewDataBinding的bind()方法系冗∞瓤郏回看一下上一部分中ActivityMainBinding代碼,我們可以知道最終會去調(diào)用ActivityMainBinding的構(gòu)造方法掌敬,具體如下:
public ActivityMainBinding(@NonNull android.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
super(bindingComponent, root, 1);
final Object[] bindings = mapBindings(bindingComponent, root, 2, sIncludes, sViewsWithIds);
this.mboundView0 = (android.widget.LinearLayout) bindings[0];
this.mboundView0.setTag(null);
this.tvName = (android.widget.TextView) bindings[1];
this.tvName.setTag(null);
setRootTag(root);
// listeners
invalidateAll();
}
我們重點(diǎn)看一下mapBindings()這個方法:
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;
}
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;
}
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;
}
}
}
if (view instanceof ViewGroup) {
final ViewGroup viewGroup = (ViewGroup) view;
final int count = viewGroup.getChildCount();
int minInclude = 0;
for (int i = 0; i < count; i++) {
final View child = viewGroup.getChildAt(i);
boolean isInclude = false;
if (indexInIncludes >= 0 && child.getTag() instanceof String) {
String childTag = (String) child.getTag();
if (childTag.endsWith("_0") &&
childTag.startsWith("layout") && childTag.indexOf('/') > 0) {
// This *could* be an include. Test against the expected includes.
int includeIndex = findIncludeIndex(childTag, minInclude,
includes, indexInIncludes);
if (includeIndex >= 0) {
isInclude = true;
minInclude = includeIndex + 1;
final int index = includes.indexes[indexInIncludes][includeIndex];
final int layoutId = includes.layoutIds[indexInIncludes][includeIndex];
int lastMatchingIndex = findLastMatching(viewGroup, i);
if (lastMatchingIndex == i) {
bindings[index] = DataBindingUtil.bind(bindingComponent, child,
layoutId);
} else {
final int includeCount = lastMatchingIndex - i + 1;
final View[] included = new View[includeCount];
for (int j = 0; j < includeCount; j++) {
included[j] = viewGroup.getChildAt(i + j);
}
bindings[index] = DataBindingUtil.bind(bindingComponent, included,
layoutId);
i += includeCount - 1;
}
}
}
}
if (!isInclude) {
mapBindings(bindingComponent, child, bindings, includes, viewsWithIds, false);
}
}
}
}
通過該方法惯豆,遞歸得到最后的bindings數(shù)組池磁。如果設(shè)置了id的,就將view變量設(shè)置為public楷兽,這樣就避免了findViewById的代碼框仔。這種方式從性能上比findViewById更加高效,因?yàn)镈ataBinding只需要遍歷一次view的數(shù)量拄养,而findViewById多次調(diào)用會遍歷多次离斩。
三、View與DataBinding之間的雙向綁定
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
}
}
我們在Activity中獲取了ActivityMainBinding實(shí)例瘪匿,那么很明顯View就持有了ViewDataBinding的實(shí)例跛梗。
通過前面DataBinding如何避免findViewById的源碼分析,我們知道在ViewDataBinding的構(gòu)造方法中會使用mapBindings()方法獲取bindings數(shù)組棋弥,繼而通過bindings數(shù)組就可以得到各個View核偿,也就是說ViewDataBinding里持有各個View的引用。
至此顽染,View與DataBinding之間的雙向綁定關(guān)系就建立起來了漾岳。
四、ViewModel與DataBinding之間的雙向綁定
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
UserViewModel model = new UserViewModel();
binding.setViewmodel(model);
}
}
我們在Activity中創(chuàng)建了ViewModel的實(shí)例粉寞,并將它設(shè)置到ViewDataBinding中去:
// ActivityMainBinding # setViewmodel
public void setViewmodel(@Nullable com.lxbnjupt.databindingdemo.UserViewModel Viewmodel) {
this.mViewmodel = Viewmodel;
synchronized(this) {
mDirtyFlags |= 0x2L;
}
notifyPropertyChanged(BR.viewmodel);
super.requestRebind();
}
由編譯時生成的ActivityMainBinding代碼可知尼荆,ActivityMainBinding將設(shè)置進(jìn)來的ViewModel賦值給了自己的成員變量,也就意味著ActivityMainBinding中持有了ViewModel唧垦。
那么捅儒,ViewModel是如何持有ViewDataBinding的呢,這個問題的分析比較復(fù)雜振亮。我們看到上面調(diào)用的setViewmodel()方法巧还,最后會去調(diào)用super.requestRebind()方法。
protected void requestRebind() {
if (mContainingBinding != null) {
mContainingBinding.requestRebind();
} else {
synchronized (this) {
if (mPendingRebind) {
return;
}
mPendingRebind = true;
}
if (USE_CHOREOGRAPHER) {
mChoreographer.postFrameCallback(mFrameCallback);
} else {
mUIThreadHandler.post(mRebindRunnable);
}
}
}
這個方法最后會輾轉(zhuǎn)調(diào)用到ActivityMainBinding的executeBindings()方法坊秸。
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
com.lxbnjupt.databindingdemo.UserViewModel viewmodel = mViewmodel;
java.lang.String viewmodelNameGet = null;
android.databinding.ObservableField<java.lang.String> viewmodelName = null;
if ((dirtyFlags & 0x7L) != 0) {
if (viewmodel != null) {
// read viewmodel.name
viewmodelName = viewmodel.name;
}
updateRegistration(0, viewmodelName);
if (viewmodelName != null) {
// read viewmodel.name.get()
viewmodelNameGet = viewmodelName.get();
}
}
// batch finished
if ((dirtyFlags & 0x7L) != 0) {
// api target 1
android.databinding.adapters.TextViewBindingAdapter.setText(this.tvName, viewmodelNameGet);
}
}
我們注意到在這個方法中創(chuàng)建了一個ObservableField對象(即viewmodelName)麸祷,并將它賦值為ViewModel中的對應(yīng)對象,之后將它作為參數(shù)去調(diào)用updateRegistration()方法褒搔。
protected boolean updateRegistration(int localFieldId, Observable observable) {
return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
}
我們先看一下這個CREATE_PROPERTY_LISTENER阶牍,它是一個CreateWeakListener對象:
private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
@Override
public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();
}
};
之后調(diào)用了updateRegistration的重載方法,在這個重載方法中又會去調(diào)用registerTo()方法:
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;
}
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()方法中會調(diào)用listenerCreator.create()方法去創(chuàng)建一個WeakListener對象荸恕。而這個方法最終會調(diào)用上面所提到的CREATE_PROPERTY_LISTENER對象的create()方法。最終創(chuàng)建了一個WeakPropertyListener對象死相,并調(diào)用它的getListener()方法獲取WeakListener對象融求。
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 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);
}
}
由源碼可知,在創(chuàng)建WeakPropertyListener對象時算撮,在其構(gòu)造方法中創(chuàng)建了WeakListener對象生宛,并將其賦值給自己的成員變量县昂,然后又通過開放getListener()方法允許外界獲得該WeakListener對象。
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;
}
public void setTarget(T object) {
unregister();
mTarget = object;
if (mTarget != null) {
mObservable.addListener(mTarget);
}
}
public boolean unregister() {
boolean unregistered = false;
if (mTarget != null) {
mObservable.removeListener(mTarget);
unregistered = true;
}
mTarget = null;
return unregistered;
}
public T getTarget() {
return mTarget;
}
protected ViewDataBinding getBinder() {
ViewDataBinding binder = get();
if (binder == null) {
unregister(); // The binder is dead
}
return binder;
}
}
我們再回到之前的registerTo()方法陷舅,該方法中在獲取到WeakListener對象之后倒彰,便調(diào)用了它的setTarget()方法。而在創(chuàng)建WeakListener對象時莱睁,其構(gòu)造方法中的ObservableReference<T>是由WeakPropertyListener中傳遞過來的待讳,所以setTarget()方法中mObservable.addListener(mTarget)會最終調(diào)用到WeakPropertyListener的addListener()方法。我們重點(diǎn)關(guān)注一下WeakPropertyListener的幾個方法:
@Override
public void addListener(Observable target) {
target.addOnPropertyChangedCallback(this);
}
@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);
}
我們知道addListener()方法中的target就是ViewModel中的ObservableField對象仰剿,它是一個被觀察者创淡。通過addOnPropertyChangedCallback()方法,相當(dāng)于向target注冊一個觀察者南吮,在數(shù)據(jù)源有變化的時候琳彩,回調(diào)onPropertyChanged()方法。在onPropertyChanged()方法中部凑,又通過WeakListener對象獲取ViewDataBinding對象露乏,之后再通過ViewDataBinding對象的handleFieldChange()方法去更新View。通過以上分析涂邀,我們可以知道其實(shí)ViewModel持有WeakPropertyListener瘟仿,WeakPropertyListener又持有WeakListener,而WeakListener又持有ViewDataBinding對象(弱引用方式)必孤,也就是ViewModel間接地持有了ViewDataBinding猾骡。
至此,ViewModel與DataBinding之間的雙向綁定關(guān)系就建立起來了敷搪。