Android DataBinding双向绑定原理

  Android中的双向绑定是指:将Model设置给View之后,当Model中的数据发生改变时,View显示的内容也会随之改变;当View发生变化时,Model中的数据也会随之改变。双向绑定可以让开发者使用数据驱动视图,并且降低了程序中的耦合度。双向绑定本质是基于观察者模式实现的。在代码层面,主要表现为:当Model中的数据发生变化时,通过回调接口,通知数据绑定器重新设置View中的数据;当View中的属性发生变化时,通过监听器捕获发生的变化,并将此变化传递给Model。这样就实现了双向绑定。整体框架如下图所示:

双向绑定举例:

  将ObservableField<String>与EditView进行双向绑定,并使用一个TextView展示。代码如下:

  ViewModel代码:

class UserViewModel:ViewModel() {
    var name:ObservableField<String> = ObservableField("")
}

  XML文件代码:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:MyView="http://schemas.android.com/apk/res-auto">

    <data>
        <variable
            name="userVM"
            type="com.example.jetpacklearn.viewModel.UserViewModel" />
    </data>
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <EditText
            android:layout_width="60dp"
            android:layout_height="wrap_content"
            android:id="@+id/userName"
            android:text="@={userVM.name}"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/showName"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/showName"
            android:text="@{userVM.name}"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toEndOf="@+id/userName"
            app:layout_constraintHorizontal_chainStyle="spread"
            app:layout_constraintEnd_toEndOf="parent"/>
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

  MainActivity文件:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        var layoutInflater = LayoutInflater.from(this)
        var dataBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(dataBinding.root)

        var userViewModel = ViewModelProvider(this).get(UserViewModel::class.java)
        dataBinding.userVM = userViewModel
    }
}

  运行结果如下:

    

接下来,以上述例子介绍双向绑定的实现流程。

双向绑定流程代码分析:

  在MainActivity中我们可以使用以下方法获取ViewDataBinding,并设置ViewModel。

var dataBinding = ActivityMainBinding.inflate(layoutInflater)
var userViewModel = ViewModelProvider(this).get(UserViewModel::class.java)
dataBinding.userVM = userViewModel

  ActivityMainBinding是自动生成的类,其inflate代码如下:

  public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater) {
    return inflate(inflater, DataBindingUtil.getDefaultComponent());
  }
  public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater,
      @Nullable Object component) {
    return ViewDataBinding.<ActivityMainBinding>inflateInternal(inflater, R.layout.activity_main, null, false, component);
  }

  接下来,会去调用ViewDataBinding的inflateInternal方法。注意,在这里DataBindingUtil.getDefaultCompinent获取的结果为null,即component对象为null。

    protected static <T extends ViewDataBinding> T inflateInternal(
            @NonNull LayoutInflater inflater, int layoutId, @Nullable ViewGroup parent,
            boolean attachToParent, @Nullable Object bindingComponent) {
        return DataBindingUtil.inflate(
                inflater,
                layoutId,
                parent,
                attachToParent,
                checkAndCastToBindingComponent(bindingComponent)
        );
    }

  接下来,调用DataBindingUtil的inflate方法。注意,这里parent为null,attachToParent为false。

    public static <T extends ViewDataBinding> T inflate(
            @NonNull LayoutInflater inflater, int layoutId, @Nullable ViewGroup parent,
            boolean attachToParent, @Nullable DataBindingComponent bindingComponent) {
        final boolean useChildren = parent != null && attachToParent;//userCHildren为false
        final int startChildren = useChildren ? parent.getChildCount() : 0;
        //生成Root View
        final View view = inflater.inflate(layoutId, parent, attachToParent);
        if (useChildren) {
            return bindToAddedViews(bindingComponent, parent, startChildren, layoutId);
        } else {
            //走这里
            return bind(bindingComponent, view, layoutId);
        }
    }

  接下来调用bind方法。注意bindingComponent为null

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

  sMapper的值如下:

public class DataBindingUtil {
    private static DataBinderMapper sMapper = new DataBinderMapperImpl();
    private static DataBindingComponent sDefaultComponent = null;
    ......
}

public class DataBinderMapperImpl extends MergedDataBinderMapper {
  DataBinderMapperImpl() {
    addMapper(new com.example.jetpacklearn.DataBinderMapperImpl());
  }
}

  DataBinderMapperImpl继承了MergedDataBinderMapper,MergedDataBinderMapper包含了一组DataBinderMapper对象。DataBinderMapper类是用来获取ViewDataBinding的,它会根据View和layoutId返回一个数据绑定类(xxxxxBinding)的实例。在DataBinderMapperImpl初始化,其会添加一个项目中自动生成的jetpacklearn.DataBinderMapperImpl类的实例。此类的代码为:

public class DataBinderMapperImpl extends DataBinderMapper {
  private static final int LAYOUT_ACTIVITYMAIN = 1;

  private static final SparseIntArray INTERNAL_LAYOUT_ID_LOOKUP = new SparseIntArray(1);

  static {
    INTERNAL_LAYOUT_ID_LOOKUP.put(com.example.jetpacklearn.R.layout.activity_main, LAYOUT_ACTIVITYMAIN);
  }

  @Override
  public ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) {
    int localizedLayoutId = INTERNAL_LAYOUT_ID_LOOKUP.get(layoutId);
    if(localizedLayoutId > 0) {
      //获取视图的Tag
      final Object tag = view.getTag();
      if(tag == null) {
        throw new RuntimeException("view must have a tag");
      }
      //根据视图的Tag生成不同的ViewDataBinding实例
      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;
  }
  ......
}

   而sMapper.getDataBinder方法最终会调用上面类中的getDataBinder方法,以生成数据绑定类的实例。在getDataBinder方法,会根据根View的tag来生成不同的ViewDataBinding对象。但是,我们在xml文件中并没有设置tag,那么这里的tag是哪里来的呢?实际上,我们在写完xml文件后,在编译时,数据绑定系统会根据我们的xml文件再生成一个XML文件,并在此XML文件中设置tag。生成的XML文件如下所示:

<androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity" 
        android:tag="layout/activity_main_0" 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:app="http://schemas.android.com/apk/res-auto" 
    xmlns:tools="http://schemas.android.com/tools" xmlns:MyView="http://schemas.android.com/apk/res-auto">

        <EditText
            android:layout_width="60dp"
            android:layout_height="wrap_content"
            android:id="@+id/userName"
            android:tag="binding_1"       
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/showName"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/showName"
            android:tag="binding_2"      
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toEndOf="@+id/userName"
            app:layout_constraintHorizontal_chainStyle="spread"
            app:layout_constraintEnd_toEndOf="parent"/>
    </androidx.constraintlayout.widget.ConstraintLayout>

  在编译器生成的文件中,不但会给根View设置tag,而且还会给里面使用数据绑定的View设置tag。

  接下来,看一下在实例化ActivityMainBindingImpl时,都会做些什么。

public class ActivityMainBindingImpl extends ActivityMainBinding  {

    @Nullable
    private static final androidx.databinding.ViewDataBinding.IncludedLayouts sIncludes;
    @Nullable
    private static final android.util.SparseIntArray sViewsWithIds;
    static {
        sIncludes = null;
        sViewsWithIds = null;
    }
    // views
    @NonNull
    private final androidx.constraintlayout.widget.ConstraintLayout mboundView0;
    ......

    public ActivityMainBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
        this(bindingComponent, root, mapBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds));
    }
    private ActivityMainBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
        super(bindingComponent, root, 1
            , (android.widget.TextView) bindings[2]
            , (android.widget.EditText) bindings[1]
            );
        this.mboundView0 = (androidx.constraintlayout.widget.ConstraintLayout) bindings[0];
        this.mboundView0.setTag(null);
        this.showName.setTag(null);
        this.userName.setTag(null);
        setRootTag(root);
        // listeners
        invalidateAll();
    }
    ......
}

   在实例化ActivityMainBindingImpl时,其做的工作如下:首先,调用mapBindings方法生成一个视图数组bindings,此视图数组中即含有根View(下标为0),亦有使用数据绑定的视图(下标参考生成的XML中的tag)。然后,调用super方法,去执行父类ViewDataBinding的构造方法。在父类的构造方法中,会构建一个进行数据绑定的Runnable对象,以在收到绑定通知时,进行数据绑定。最后,将ActivityMainBindingIMpl实例放到根View的tag里,并发送一个数据绑定请求。接下来,依次对上面3个工作进行详细分析。

   1、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;
        //还没将数据绑定实例放到view的Tag中,此时getBinding的返回值为null
        final ViewDataBinding existingBinding = getBinding(view);
        if (existingBinding != null) {
            return;
        }
        //获取视图的TAG,参考生成的XML文件
        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)) {
                //从生成的XML文件中知,index为0
                final int index = parseTagInt(tag, underscoreIndex + 1);
                if (bindings[index] == null) {
                    //添加根视图
                    bindings[index] = view;
                }
                //在案例中,未使用include标签,此值为-1
                indexInIncludes = includes == null ? -1 : index;
                isBound = true;
            } else {
                indexInIncludes = -1;
            }
        } else if (tag != null && tag.startsWith(BINDING_TAG_PREFIX)) {
            //将子View放到bindings数组中
            //生成的XML文件中,子View的tag格式为binding_{x},此时tagIndex的值就是x
            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;
        }
        ......
        //将xml文件中的子View放到bindings数组中
        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;
                //未使用include标签,此处先略过
                if (indexInIncludes >= 0 && child.getTag() instanceof String) {
                    ......
                }
                if (!isInclude) {
                    //递归调用当前方法,传入child,且isRoot为false以绑定子视图
                    mapBindings(bindingComponent, child, bindings, includes, viewsWithIds, false);
                }
            }
        }
    }

  2、super方法对应的父类ViewDataBinding的构造方法如下:

    protected ViewDataBinding(DataBindingComponent bindingComponent, View root, int localFieldCount) {
        mBindingComponent = bindingComponent;
        //此数组用于Observable数据变化时的回调
        mLocalFieldObservers = new WeakListener[localFieldCount];
        this.mRoot = root;
        if (Looper.myLooper() == null) {
            throw new IllegalStateException("DataBinding must be created in view's UI Thread");
        }
        
        if (USE_CHOREOGRAPHER) {
            //SDK>16时走这里
            mChoreographer = Choreographer.getInstance();
            mFrameCallback = new Choreographer.FrameCallback() {
                @Override
                public void doFrame(long frameTimeNanos) {
                    mRebindRunnable.run();
                }
            };
        } else {
            mFrameCallback = null;
            mUIThreadHandler = new Handler(Looper.myLooper());
        }
    }

    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();
        }
    };

 3、将绑定类的实例放到根View的Tag,并发送绑定请求

        setRootTag(root);
        // listeners
        invalidateAll();

  setRootTag代码如下:

    protected void setRootTag(View view) {
        view.setTag(R.id.dataBinding, this);
    }

  invalidateAll方法代码如下:

    public void invalidateAll() {
        synchronized(this) {
                mDirtyFlags = 0x4L;
        }
        requestRebind();
    }
    protected void requestRebind() {
        if (mContainingBinding != null) {
            mContainingBinding.requestRebind();
        } else {
            final LifecycleOwner owner = this.mLifecycleOwner;

            ......

            synchronized (this) {
                if (mPendingRebind) {
                    return;
                }
                mPendingRebind = true;
            }
            if (USE_CHOREOGRAPHER) {
                //SDK>16走这里
                //发送数据绑定请求,最终会调用mRebindRunnable的executePendingBindings方法
                mChoreographer.postFrameCallback(mFrameCallback);
            } else {
                mUIThreadHandler.post(mRebindRunnable);
            }
        }
    }

调用的executePendingBindings方法的代码如下:

    public void executePendingBindings() {
        if (mContainingBinding == null) {
            //走这里
            executeBindingsInternal();
        } else {
            //include标签下的View走这里
            mContainingBinding.executePendingBindings();
        }
    }

    private void executeBindingsInternal() {
        if (mIsExecutingPendingBindings) {
            requestRebind();
            return;
        }
        if (!hasPendingBindings()) {
            return;
        }
        mIsExecutingPendingBindings = true;
        mRebindHalted = false;

        ......

        if (!mRebindHalted) {
            //调用此方法进行数据绑定,此方法的真正实现在生成的xxxxBindingImpl类中(如ActivityMainBindingImpl)
            executeBindings();
            if (mRebindCallbacks != null) {
                mRebindCallbacks.notifyCallbacks(this, REBOUND, null);
            }
        }
        mIsExecutingPendingBindings = false;
    }

  executeBindings方法的代码如下:

    protected void executeBindings() {
        long dirtyFlags = 0;
        synchronized(this) {
            dirtyFlags = mDirtyFlags;
            mDirtyFlags = 0;
        }
        com.example.jetpacklearn.viewModel.UserViewModel userVM = mUserVM;
        androidx.databinding.ObservableField<java.lang.String> userVMName = null;
        java.lang.String userVMNameGet = null;

        if ((dirtyFlags & 0x7L) != 0) {



                if (userVM != null) {
                    // read userVM.name
                    userVMName = userVM.getName();
                }
                //给userVMName添加一个数据变化的监听器;实现双向绑定的步骤1
                updateRegistration(0, userVMName);


                if (userVMName != null) {
                    // read userVM.name.get()
                    userVMNameGet = userVMName.get();
                }
        }
        // batch finished
        if ((dirtyFlags & 0x7L) != 0) {
            // api target 1

            androidx.databinding.adapters.TextViewBindingAdapter.setText(this.showName, userVMNameGet);
            androidx.databinding.adapters.TextViewBindingAdapter.setText(this.userName, userVMNameGet);
        }
        if ((dirtyFlags & 0x4L) != 0) {
            // api target 1

           //给userName(EditView)添加文本监听器;实现双向绑定的步骤2 
androidx.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.userName, (androidx.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.OnTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.AfterTextChanged)null, userNameandroidTextAttrChanged);
        }
    }

  为了实现双向绑定,既需要为数据设置监听器,监听数据变化;也需要为View设置监听器,以监听View的变化。在此,通过updateRegistration方法对数据设置监听器;通过最后一行的setTextWatcher方法对View设置监听器。因此,主要介绍中updateRegistration方法和setTextWatcher中的参数userNameAndroidTextAttrChanged。这两部分是实现双向绑定的关键。先介绍updateRegistration方法。

  3.1 给Modle设置监听器

  updateRegistration方法代码如下:

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

  CREATE_PROPERTY_LISTENER的作用是 为Observable类添加一个回调接口,此接口会在数据变化时,通知观察者。其代码如下:

    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) {
            //创建一个WeakListener对象,此对象在给Observable类设置回调接口时,起到承接作用。
            //WeakListener中包含了ViewDataBinding实例,回调接口的引用(当前类)
            mListener = new WeakListener<Observable>(binder, localFieldId, this);
        }

        @Override
        public WeakListener<Observable> getListener() {
            return mListener;
        }

        @Override
        public void addListener(Observable target) {
            //给Observable添加回调接口
            target.addOnPropertyChangedCallback(this);
        }

        ......

        //Observable类的子类会在其set方法中调用此方法
        @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);
        }
    }

   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) {
            //binder为ViewDataBinding实例
            super(binder, sReferenceQueue);
            //标识ViewDataBinding中的Observable字段的ID
            mLocalFieldId = localFieldId;
            //实现了回调接口OnPropertyChangedCallback
            mObservable = observable;
        }
        ......
}

   handleFieldChange代码如下:

    private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
        ......

        //调用ActivityMainBinding的onFieldChange方法,标注哪些数据发送了变化
        boolean result = onFieldChange(mLocalFieldId, object, fieldId);
        if (result) {
            //请求重新进行数据绑定
            requestRebind();
        }
    }

  updateRegistration(localFIeldId,observable,CREATE_PROPERTY_LISTENER)的代码如下:

    private boolean updateRegistration(int localFieldId, Object observable,
            CreateWeakListener listenerCreator) {
        if (observable == null) {
            return unregisterFrom(localFieldId);
        }
        WeakListener listener = mLocalFieldObservers[localFieldId];
        if (listener == null) {
            //给observable设置回调接口OnPropertyChangedCallback
            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;
    }

  registerTo方法代码如下:

    protected void registerTo(int localFieldId, Object observable,
            CreateWeakListener listenerCreator) {
        if (observable == null) {
            return;
        }
        WeakListener listener = mLocalFieldObservers[localFieldId];
        if (listener == null) {
            //生成一个WeakListener对象,此对象包含了OnPropertyChangedCallback的引用
            listener = listenerCreator.create(this, localFieldId);
            mLocalFieldObservers[localFieldId] = listener;
            if (mLifecycleOwner != null) {
                listener.setLifecycleOwner(mLifecycleOwner);
            }
        }
        //给observable添加回调接口(OnPropertyChangedCallback)
        listener.setTarget(observable);
    }

   setTarget方法的代码如下:

        public void setTarget(T object) {
            unregister();
            mTarget = object;
            if (mTarget != null) {
                //mObservable为WeakPropertyListener类实例,其addListener代码在上面
                //mTarget是一个observable对象,此方法作用是给此对象添加一个回调接口
                mObservable.addListener(mTarget);
            }
        }

  题外话:在第一次看到此代码时,感觉很绕,回调很多,使人不容易看懂,觉着为什么不直接使用以下这样的写法:

userVMName.addOnPropertyChangedCallback(new XXXXCallBack())

  这样的写法,可以让人一看就懂。一眼就能看出此行代码的作用是什么,添加了什么样的回调接口。而官方代码则看的绕来绕去的,不易理解。后来思考了以下,发现官方的写法其实挺有道理的。官方代码的写法使代码的耦合度降低了,符合“开闭原则”,“依赖倒置原则”。而我们的简单写方法则不符合这两个原则。比如,当我们要更换一个新的Callback时,简单写法需要修改addxxxCallback方法的参数,而官方的代码,则采用扩展的方法实现这种需求,不需要对原来代码进行修改。即只需要重新实现一个CreateListener的类,并添加一个新的updateRegistration方法就好。这样虽然多写了一些代码,但是保证了“开闭原则”,即对修改关闭,对扩展开放。

  在此,通过下面的类图再回顾一下给Observable添加PropertyChangedCallback的过程。首先,创建一个WeakListener对象。然后,调用此对象的setTarget(Observable)方法,给Observable添加回调接口。此过程的核心是WeakListener类,此类是一个“虚假”的监听器类,其并没有实现监听功能。而是持有了回调接口的引用,并负责给Observable对象设置回调接口。官方将“给Observable对象设置回调接口”这个功能抽象为了一个接口ObservableReference,此举符合“依赖倒置”原则,方便日后对代码进行扩展。

   3.2、给View设置监听器

  上面介绍了如何给Model设置监听器,接下来介绍如何给View设置监听器。以上面的案例代码为例,绑定系统通过setTextWatcher给EditView设置监听器,以在EditView的文字变化时,通知Model去同步数据,setTextWatcher方法如下:


    protected void executeBindings() {

        ......

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

            androidx.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.userName, (androidx.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.OnTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.AfterTextChanged)null, userNameandroidTextAttrChanged);
        }
    }

  userNameandroidTextAttrChanged对应的类的代码如下:

    private androidx.databinding.InverseBindingListener userNameandroidTextAttrChanged = new androidx.databinding.InverseBindingListener() {
        @Override
        public void onChange() {
            //EditView的文本变化时会调用此方法
            java.lang.String callbackArg_0 = androidx.databinding.adapters.TextViewBindingAdapter.getTextString(userName);

            boolean userVMNameJavaLangObjectNull = false;
            // userVM
            com.example.jetpacklearn.viewModel.UserViewModel userVM = mUserVM;
            // userVM != null
            boolean userVMJavaLangObjectNull = false;
            // userVM.name
            androidx.databinding.ObservableField<java.lang.String> userVMName = null;
            // userVM.name.get()
            java.lang.String userVMNameGet = null;

            userVMJavaLangObjectNull = (userVM) != (null);
            if (userVMJavaLangObjectNull) {

                userVMName = userVM.getName();

                userVMNameJavaLangObjectNull = (userVMName) != (null);
                if (userVMNameJavaLangObjectNull) {
                    //将EditView的text赋值给userVMName
                    userVMName.set(((java.lang.String) (callbackArg_0)));
                }
            }
        }
    };

    userVMName的set方法代码如下:

public class ObservableField<T> extends BaseObservableField implements Serializable {
    static final long serialVersionUID = 1L;
    private T mValue;

    ......

    /**
     * Set the stored value.
     *
     * @param value The new value
     */
    public void set(T value) {
        if (value != mValue) {
            //避免无线循环,只有数据真正改变时,才通知数据绑定器进行数据绑定
            mValue = value;
            notifyChange();
        }
    }
}

   至此,双向绑定原理的介绍基本结束。其本质可以看作分别给数据源Model和视图View设置监听器,以在数据变化时,通知彼此同步变化。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值