Android架構(gòu)組件之DataBinding源碼解析

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)系就建立起來了敷搪。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市幢哨,隨后出現(xiàn)的幾起案子赡勘,更是在濱河造成了極大的恐慌,老刑警劉巖捞镰,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件闸与,死亡現(xiàn)場離奇詭異,居然都是意外死亡岸售,警方通過查閱死者的電腦和手機(jī)践樱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來凸丸,“玉大人拷邢,你說我怎么就攤上這事∈郝” “怎么了瞭稼?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵忽洛,是天一觀的道長。 經(jīng)常有香客問我环肘,道長欲虚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任悔雹,我火速辦了婚禮复哆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘腌零。我一直安慰自己寂恬,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布莱没。 她就那樣靜靜地躺著初肉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪饰躲。 梳的紋絲不亂的頭發(fā)上牙咏,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機(jī)與錄音嘹裂,去河邊找鬼妄壶。 笑死,一個胖子當(dāng)著我的面吹牛寄狼,可吹牛的內(nèi)容都是我干的丁寄。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼泊愧,長吁一口氣:“原來是場噩夢啊……” “哼伊磺!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起删咱,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤屑埋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后痰滋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體摘能,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年敲街,在試婚紗的時候發(fā)現(xiàn)自己被綠了团搞。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡多艇,死狀恐怖逻恐,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤梢莽,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布萧豆,位于F島的核電站,受9級特大地震影響昏名,放射性物質(zhì)發(fā)生泄漏涮雷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一轻局、第九天 我趴在偏房一處隱蔽的房頂上張望洪鸭。 院中可真熱鬧,春花似錦仑扑、人聲如沸览爵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蜓竹。三九已至,卻和暖如春储藐,著一層夾襖步出監(jiān)牢的瞬間俱济,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工钙勃, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蛛碌,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓辖源,卻偏偏與公主長得像蔚携,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子克饶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345

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