JetPack 之 DataBinding

DataBinding 是一个基于 APT 技术的MVVM框架,DataBinding 使用了观察者模式来完成双向通知,通过 APT 技术在编译时扫描 XML 布局中的标记生成一个继承自 ViewDataBinding 的子类并写入相关绑定代码,保存在build目录下,不同版本的 android studio 具体路径不一,可以通过切换成android工程目录模式后在java(generated)目录下查找,保存类名为布局名+BindingImpl(如上面的例子layout_user.xml 对应 LayoutUserBindingImpl)。并建立相关绑定事件ID(在BR资源中存放),通过ID可以主动发起相关更新事件通知。ViewDataBinding 内部维护了观察者注册与通知并且持有 所有 View 对象以及绑定注册的model对象。

DataBinding 原理浅析

基本用法

1.在build.gradel中配置DataBinding

android {
    ...
    dataBinding{
        enabled true
    }
}

2.对View的布局文件进行配置,使用layout作为跟布局标签。使用data标签进行DataBinding的绑定。控件中可以使用@{}进行绑定,@{}为单向绑定@={}为双向绑定



    
    
        
        
    
    
        
        
        
    

3.声明一个数据类,使用ObservableField声明属性并通过范型指定数据类型。

public class UserInfo {
    public ObservableField userName = new ObservableField<>();
    public ObservableField passWord = new ObservableField<>();
}

4.在Activity中进行绑定

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        var binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        var userInfo = UserInfo()
        userInfo.userName.set("xixi")
        userInfo.passWord.set("123")
        binding.userInfo = userInfo
    }
}

DataBinding绑定流程

DataBinding使用了apt技术,我们build项目时DataBinding会生成多个文件,我们可以在build文件中查看。

  • DataBinding将原有的activity_main.xml文件进行了拆分,分别是activity_mian.xmlactivity_main-layout.xml

build => intermediates => data_binding_layout_info_type_package => debug => packageDebugResources => out
可以看到生成的activity_main-layout.xml

build => intermediates => package_res => debug => layout =>
可以看到生成的activity_main.xml

找不到位置的也没关系,
首先DataBinding将初始layout文件的根元素LinearLayout 和那些在属性中使用了binding表达式 的view都被设置了 Tag,这就是activity_main.xml中的内容:

...

...

然后DataBinding把最初布局文件中的以及各个view中的 binding表达式 内容抽取出来,生成了 activtiy_main-layout.xml文件,其内容类似如下代码:


    
        


        

xml
 
        
            
                
                    
                    false
                    
                
            
            
        
    

标签的里面,毫无疑问,有标签和标签。它们也包含位置location信息。标签告诉ViewModel应该映射到哪个View,绑定类型为单向还是双向,View的位置及View值的位置。
我们有一个标签的列表。在布局文件中我们可以有多个View/ViewGroup,但只有包含data-binding表达式的View/ViewGroup才会出现在这里。标签里面的标签表示Viewdata-binding表达式。

接着,DataBinding还生成了ActivityMainBinding.javaActivityMainBindingImpl.java

build => generated => ap_generated_sources => debug => out
=> activity的包名.databinding :
可以看到生成的DataBinding文件 ActivityMainBindingImpl

build => data_binding_base_class_source_out => debug => dataBindingGenBaseClassesDebug => out => activity的包名.databinding :
可以看到生成的DataBinding文件ActivityMainBinding

回到使用代码中生成ActivityMainBinding的那行代码:

var binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)

查看DataBindingUtil.setContentView:

public static  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);
    }

方法中通过activity的setContentView加载布局,并通过window找到id为contentViewGroup,它是一个FrameLayout用于加载我们添加的布局文件。接下来就是bindToAddedViews方法:

private static  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);
        }
    }

parent中的子View就是我们布局文件中的根布局LinearLayout,所以走的是if中的代码

    private static DataBinderMapper sMapper = new DataBinderMapperImpl();
static  T bind(DataBindingComponent bindingComponent, View[] roots,
            int layoutId) {
        return (T) sMapper.getDataBinder(bindingComponent, roots, layoutId);
    }

我们还有一种通过DataBindingUtil.inflate();来生成ViewDataBinding类的方式。其内部依然是调用到了bind()方法中。

在bind()方法中又调用了DataBinderMapperImpl中的getDataBinder方法。

#DataBinderMapperImpl.getDataBinder


通过一系列的条件判断之后返回了一个ActivityMainBindingImpl对象,接下来我们看它的构造方法。

    private ActivityLoginBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
        super(bindingComponent, root, 7
            , (android.widget.LinearLayout) bindings[13]
            , (androidx.appcompat.widget.AppCompatCheckBox) bindings[9]
            , (androidx.appcompat.widget.AppCompatEditText) bindings[5]
            , (androidx.appcompat.widget.AppCompatImageView) bindings[6]
            );
        this.checkBox.setTag(null);
        this.etPassword.setTag(null);
        this.ivSwichPasswrod.setTag(null);
        this.mboundView0 = (android.widget.LinearLayout) bindings[0];
        this.mboundView0.setTag(null);
        this.mboundView1 = (androidx.appcompat.widget.AppCompatImageView) bindings[1];
        this.mboundView1.setTag(null);
        this.mboundView10 = (androidx.appcompat.widget.AppCompatTextView) bindings[10];
        this.mboundView10.setTag(null);
        this.mboundView11 = (androidx.appcompat.widget.AppCompatTextView) bindings[11];
        this.mboundView11.setTag(null);
        this.mboundView12 = (androidx.appcompat.widget.AppCompatTextView) bindings[12];
        this.mboundView12.setTag(null);
        this.mboundView2 = (androidx.appcompat.widget.AppCompatEditText) bindings[2];
        this.mboundView2.setTag(null);
        this.mboundView3 = (androidx.appcompat.widget.AppCompatImageView) bindings[3];
        this.mboundView3.setTag(null);
        this.mboundView4 = (androidx.appcompat.widget.AppCompatImageView) bindings[4];
        this.mboundView4.setTag(null);
        this.mboundView7 = (androidx.appcompat.widget.AppCompatTextView) bindings[7];
        this.mboundView7.setTag(null);
        this.mboundView8 = (androidx.appcompat.widget.AppCompatButton) bindings[8];
        this.mboundView8.setTag(null);
        setRootTag(root);
        // focus
        invalidateAll();
    }

我这里查看的是登录页面,但是原理是一样的。
它就是将布局中的含有databinding赋值的tag控件一一存入bindings的Object的数组中并返回。注意focus处的代码:

 @Override
    public void invalidateAll() {
        synchronized(this) {
                mDirtyFlags = 0x100L;
        }
        requestRebind();
    }

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

最终会执行mRebindRunnable的run():


 private final Runnable mRebindRunnable = new Runnable() {
        @Override
        public void run() {
            ... 重点看这一句
            executePendingBindings();
        }
    };

public void executePendingBindings() {
        if (mContainingBinding == null) {
            executeBindingsInternal();
        } else {
            mContainingBinding.executePendingBindings();
        }
    }

public void executeBindingsInternal(){
  ...
  executeBindings();
  ...
}

最重要的是executeBindings

java
@Override
    protected void executeBindings() {
       long dirtyFlags = 0;
        synchronized(this) {
            dirtyFlags = mDirtyFlags;
            mDirtyFlags = 0;
        }
        com.example.main.MainViewModel viewModel = mViewModel;
        androidx.databinding.ObservableField viewModelLoginErrorTip = null;
        java.lang.String viewModelLoginErrorTipGet = null;

        int isVisibleViewModelVI = 0;
        boolean isVisibleViewModel = false;
        java.lang.String textViewModel = null;
        android.databinding.ObservableBoolean isVisibleViewModel1 = null;
        android.databinding.ObservableField textViewModel1 = null;

        if ((dirtyFlags & 0xfL) != 0) {
              // focus--1
              if ((dirtyFlags & 0x181L) != 0) {
                    if (viewModel != null) {
                        // read viewModel.loginErrorTip
                        viewModelLoginErrorTip = viewModel.loginErrorTip;
                    }
                    updateRegistration(0, viewModelLoginErrorTip);
                    if (viewModelLoginErrorTip != null) {
                        // read viewModel.loginErrorTip.get()
                        viewModelLoginErrorTipGet = viewModelLoginErrorTip.get();
                    }
            }

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

                    if (viewModel != null) {
                        // read viewModel.isVisible
                        isVisibleViewModel1 = viewModel.isVisible;
                    }
                    updateRegistration(1, isVisibleViewModel1);


                    if (isVisibleViewModel1 != null) {
                        // read viewModel.isVisible.get()
                        isVisibleViewModel = isVisibleViewModel1.get();
                    }
                    if((dirtyFlags & 0xbL) != 0) {
                        if (isVisibleViewModel) {
                            dirtyFlags |= 0x20L;
                        } else {
                            dirtyFlags |= 0x10L;
                        }}
                    isVisibleViewModelVI = (isVisibleViewModel) ? (android.view.View.VISIBLE) : (android.view.View.INVISIBLE);
            }
            if ((dirtyFlags & 0xdL) != 0) {

                    if (viewModel != null) {
                        // read viewModel.text
                        textViewModel1 = viewModel.text;
                    }
                    updateRegistration(2, textViewModel1);


                    if (textViewModel1 != null) {
                        // read viewModel.text.get()
                        textViewModel = textViewModel1.get();
                    }
            }
        }

       if ((dirtyFlags & 0x181L) != 0) {
            // api target 1
            // focus --2
            androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView7, viewModelLoginErrorTipGet);
        }
        // batch finished
        if ((dirtyFlags & 0xbL) != 0) {
            // api target 1

            this.mboundView2.setVisibility(isVisibleViewModelVI);
        }
        if ((dirtyFlags & 0xdL) != 0) {
            // api target 1   android.databinding.adapters.TextViewBindingAdapter.setText(this.tvTest, textViewModel);
        }
    }

可以看到正是在这个方法中将viewModel中的ObsrvableField类型中的值设置在了对应的控件上。(注意focus--1 和 focus2--处的代码)

该方法总的来说做了三件事情:

1.创建并设置回调,如
android:onClick="@{presenter::saveUserName}这种表达式,会在presenter不为空的情况下,创建对应的回调,并设置到mboundView4上;

  1. 将数据模型上的值更新到UI上,如将firstName设置到mboundView1上,lastName设置到mboundView2上。可以看到,每一个标签声明的变量都有一个专属的标记位,当改变量的值被更新时,对应的脏标记位就会置为1,executeBindings的时候变回将这些变动更新到对应的控件。
  1. 在设置了双向绑定的控件上,为其添加对应的监听器,监听其变动,如:EditText上设置TextWatcher。具体的设置逻辑放置到了TextViewBindingAdapter.setTextWatcher里。源码如下,也就是创建了一个新的TextWatcher,将我们传进来的监听器包裹在其中。在这里看到了@BindingAdapter注解,这个注解实现了控件属性和代码内的方法调用的映射,编译期,数据绑定框架通过这种方式,为对应的控件生成对应的方法调用。
@BindingAdapter(value = {"android:beforeTextChanged", "android:onTextChanged",
            "android:afterTextChanged", "android:textAttrChanged"}, requireAll = false)
    public static void setTextWatcher(TextView view, final BeforeTextChanged before,
            final OnTextChanged on, final AfterTextChanged after,
            final InverseBindingListener textAttrChanged) {
        final TextWatcher newValue;
        if (before == null && after == null && on == null && textAttrChanged == null) {
            newValue = null;
        } else {
            newValue = new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                    if (before != null) {
                        before.beforeTextChanged(s, start, count, after);
                    }
                }

                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    if (on != null) {
                        on.onTextChanged(s, start, before, count);
                    }
                    if (textAttrChanged != null) {
                        textAttrChanged.onChange();
                    }
                }

                @Override
                public void afterTextChanged(Editable s) {
                    if (after != null) {
                        after.afterTextChanged(s);
                    }
                }
            };
        }
        final TextWatcher oldValue = ListenerUtil.trackListener(view, newValue, R.id.textWatcher);
        if (oldValue != null) {
            view.removeTextChangedListener(oldValue);
        }
        if (newValue != null) {
            view.addTextChangedListener(newValue);
        }
    }
小结一下DataBinding的绑定过程

DataBinding渲染处理后的布局文件生成ActivityMainBindingImpl实例对象,在其构造函数中调用invalidateAll引发数据绑定。

数据更新与View更新

数据更新通知View

继续回到executeBindings中focus--处的代码:

 if (viewModel != null) {
                        // read viewModel.loginErrorTip
                        viewModelLoginErrorTip = viewModel.loginErrorTip;
                    }
                    updateRegistration(0, viewModelLoginErrorTip);

先从viewModel中获取属性loginErrorTip,在LoginViewModel中它是这么定义的:

public ObservableField loginErrorTip = new ObservableField<>();

然后,对ViewModel中定义的这个值也就是viewModelLoginErrorTip注册了监听。它是ObservableField类型,也是最后设置到xml中的变量,我们修改它的值,view中就能够得到通知,那么是怎么实现的呢?
进入updateRegistration():

#ViewDataBinding.java
protected boolean updateRegistration(int localFieldId, Observable observable) {
        return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
    }

 private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
        @Override
        public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
            return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();
        }
    };

CREATE_PROPERTY_LISTENER是WeakPropertyListener对象的工厂,负责创建一个WeakPropertyListener对象,继续看WeakPropertyListener:

    private static class WeakPropertyListener extends Observable.OnPropertyChangedCallback
            implements ObservableReference {
        final WeakListener mListener;

        public WeakPropertyListener(ViewDataBinding binder, int localFieldId) {
            mListener = new WeakListener(binder, localFieldId, this);
        }

        @Override
        public WeakListener 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);
        }
    }

WeakPropertyListener内有持有变量WeakListener,能通过它的getListener()得到WeakListener。

 private static class WeakListener extends WeakReference {
        private final ObservableReference mObservable;
        protected final int mLocalFieldId;
        private T mTarget;

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

        public void setLifecycleOwner(LifecycleOwner lifecycleOwner) {
            mObservable.setLifecycleOwner(lifecycleOwner);
        }

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

原来它是一个弱引用,它持有ViewDataBinding以及mTarget(泛型T即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维持了从localFieldIdWeakListener的映射。先从mLocalFieldObserverslocalFieldId对应的WeakListener,如果为null的话,就调用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);
    }

通过CreateWeakListener.create()得到WeakListener之后,registerTo()WeakListener存储在mLocalFieldObservers中。然后调用WeakListenersetTarget()

listener是WeakListener对象,查看WeakListener.setTarger:

#WeakListener.java

private final ObservableReference mObservable;
public WeakListener(ViewDataBinding binder, int localFieldId,
                ObservableReference observable) {
            super(binder, sReferenceQueue);
            mLocalFieldId = localFieldId;
            mObservable = observable;
        }
 public void setTarget(T object) {
            unregister();
            mTarget = object;
            if (mTarget != null) {
                mObservable.addListener(mTarget);
            }
        }

mTager是被观察者,在我们例子中是updateRegistration中传进来的viewModelLoginErrorTip,也就是viewModel中定义的ObservableField对象
mObservableWeakPropertyListener对象,是构建WeakListener时传进来的,WeakListener构造函数是WeakPropertyListener构造函数中传进来的

 public WeakPropertyListener(ViewDataBinding binder, int localFieldId) {
            mListener = new WeakListener(binder, localFieldId, this);
        }

WeakPropertyListener对象是在CREATE_PROPERTY_LISTENER.create中创建的:

private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
        @Override
        public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
            return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();
        }
    };

create是在registerTo中调用的。
通过 CreateWeakListener.create()得到WeakListener之后,registerTo()WeakListener存储在mLocalFieldObservers中。然后调用WeakListenersetTarget()

查看WeakPropertyListener.addListener

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

targetObservableFiledthisWeakPropertyListener
addOnPropertyChangedCallback的具体实现是在BaseObservable,ObservableFiled继承了BaseObservable

#BaseObservable.java
 private transient PropertyChangeRegistry mCallbacks;
...
  @Override
    public void addOnPropertyChangedCallback(@NonNull OnPropertyChangedCallback callback) {
        synchronized (this) {
            if (mCallbacks == null) {
                mCallbacks = new PropertyChangeRegistry();
            }
        }
        mCallbacks.add(callback);
    }

addOnPropertyChangedCallback里,将WeakPropertyListener加入到了mCallbacks(PropertyChangeRegistry)里面,PropertyChangeRegistry继承了CallbackRegistry,其内部有一个List mCallbacksWeakPropertyListener 继承了Observable.OnPropertyChangedCallback

这样最终是将WeakPropertyListener监听添加到了PropertyChangeRegistry的mCallbacks中。`也就是将数据变化的监听加入到了集合中。

那么监听什么时候回调的?
查看ObservableField.set

 public void set(T value) {
        if (value != mValue) {
            mValue = value;
            notifyChange();
        }
    }

当我们set值时,会调用到notifyChange(),最后会调用到NotifierCallback.onNotifyCallback(),NotifierCallback是抽象类,其实现类是:

# PropertyChangeRegistry.java

private static final CallbackRegistry.NotifierCallback NOTIFIER_CALLBACK = new CallbackRegistry.NotifierCallback() {
        @Override
        public void onNotifyCallback(Observable.OnPropertyChangedCallback callback, Observable sender,
                int arg, Void notUsed) {
            callback.onPropertyChanged(sender, arg);
        }
    };

因此调用mNotifier.onNotifyCallback实际上就是调用Observable.OnPropertyChangedCallback .onPropertyChanged(),我们进一步看看:

private static class WeakPropertyListener extends Observable.OnPropertyChangedCallback
            implements ObservableReference {
    final WeakListener mListener;

    // …… 

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

再进入ViewDataBinding的handleFieldChange():

    private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
        boolean result = onFieldChange(mLocalFieldId, object, fieldId);
        if (result) {
            requestRebind();
        }
    }

onFieldChange方法是ViewDataBinding的抽象方法,其实现类:

@Override
    protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
        switch (localFieldId) {
            case 0 :
                return onChangeViewModelLoginErrorTip((androidx.databinding.ObservableField) object, fieldId);
      ...
}

private boolean onChangeViewModelLoginErrorTip(androidx.databinding.ObservableField ViewModelLoginErrorTip, int fieldId) {
        if (fieldId == BR._all) {
            synchronized(this) {
                    mDirtyFlags |= 0x1L;
            }
            return true;
        }
        return false;
    }

可以看到修改了标志位。
可以看到onPropertyChanged调用了编译生成的ActivityLogingBindingImpl的onFieldChange方法将mDirtyFlags标识为需要重新绑定VM到V上,并且调用requestRebind方法。requestRebind方法前面已经介绍过,最后能调用到ActivityLogingBindingImpl的executeBindings()进行界面绑定的工作。

简化版

层层调用实在是太深了。我们稍微简化一下,其内部依然是观察者模式:
首先在DataBinding实现类中:

public class DemoDataBinding extends ViewDataBinding {
    CustomObservableField name = new CustomObservableField<>();
    AppCompatTextView textView;

    public DemoDataBinding(DataBindingComponent bindingComponent, View root, int localFieldCount) {
        super(bindingComponent, root, localFieldCount);
        textView = new AppCompatTextView(root.getContext());
        updateRegistration(0,name);
    }

    protected boolean updateRegistration(int localFieldId, CustomObservableField observable) {
        WeakPropertyListener weakPropertyListener = new WeakPropertyListener(this,localFieldId);

        observable.addOnPropertyChangedCallback(weakPropertyListener);
        return true;
    }
    
    // 为控件赋值
    public void executeBindingsLayout() {
        String nameStr = name.getName();
        textView.setText(nameStr);
    }
}

被观察者 - CustomObservableField。观察者 - ViewDataBinding

public class CustomObservableField implements CustomObservable{
    private List mCallbacks = new ArrayList<>();
    private T name;

    public void setName(T name) {
        this.name = name;
        notifyCalls(mCallbacks);
    }

    public T getName() {
        return name;
    }

    public void addOnPropertyChangedCallback(@NonNull CustomObservable.OnPropertyChangedCallback callback) {
        mCallbacks.add((WeakPropertyListener) callback);
    }

    @Override
    public void removeOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
        mCallbacks.remove(callback);
    }

    void notifyCalls(List mCallbacks){
        for(WeakPropertyListener callback:mCallbacks){
            callback.onPropertyChanged();
        }
    }

}

public interface CustomObservable {
    void addOnPropertyChangedCallback(CustomObservable.OnPropertyChangedCallback callback);
    void removeOnPropertyChangedCallback(CustomObservable.OnPropertyChangedCallback callback);
    abstract class OnPropertyChangedCallback {
        public abstract void onPropertyChanged();
    }
}
public class WeakPropertyListener extends CustomObservable.OnPropertyChangedCallback{
    private ViewDataBinding viewDataBinding;

    public WeakPropertyListener(ViewDataBinding binder, int localFieldId) {
        this.viewDataBinding = binder;
    }

    public void onPropertyChanged() {
        DemoDataBinding binder = (DemoDataBinding) viewDataBinding;
        binder.executeBindingsLayout();
    }
}

第一步,我们在DataBinding实现类的构造函数中调用updateRegistration为我们的Observable和Observer之间创建绑定关系。

第二步,调用Observable的set()方法时通知Observer进行更新。

View中的值改变通知数据更新

这种情况往往是编辑框更改了输入文字,则ViewModel中承载着文字的ObservableField变量中的值也发生了改变,因此一般我们使用编辑框时会用双向绑定的binding表达式:android:text = @={viewModel.name}

回到ActivityLoginBindingImpl的实现类的executeBindings方法中:

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

            androidx.databinding.adapters.CompoundButtonBindingAdapter.setListeners(this.checkBox, (android.widget.CompoundButton.OnCheckedChangeListener)null, checkBoxandroidCheckedAttrChanged);
            androidx.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.etPassword, (androidx.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.OnTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.AfterTextChanged)null, etPasswordandroidTextAttrChanged);
            androidx.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.mboundView2, (androidx.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.OnTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.AfterTextChanged)null, mboundView2androidTextAttrChanged);
        }

在这个方法里调用了setTextWatcher去监听EditTextView的TextWatcher:

    @BindingAdapter(value = {"android:beforeTextChanged", "android:onTextChanged",
            "android:afterTextChanged", "android:textAttrChanged"}, requireAll = false)
    public static void setTextWatcher(TextView view, final BeforeTextChanged before,
            final OnTextChanged on, final AfterTextChanged after,
            final InverseBindingListener textAttrChanged) {
        final TextWatcher newValue;
        if (before == null && after == null && on == null && textAttrChanged == null) {
            newValue = null;
        } else {
            newValue = new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                    if (before != null) {
                        before.beforeTextChanged(s, start, count, after);
                    }
                }

                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    if (on != null) {
                        on.onTextChanged(s, start, before, count);
                    }
                    // focus--1 
                    if (textAttrChanged != null) {
                        textAttrChanged.onChange();
                    }
                }

                @Override
                public void afterTextChanged(Editable s) {
                    if (after != null) {
                        after.afterTextChanged(s);
                    }
                }
            };
        }
        final TextWatcher oldValue = ListenerUtil.trackListener(view, newValue, R.id.textWatcher);
        if (oldValue != null) {
            view.removeTextChangedListener(oldValue);
        }
        if (newValue != null) {
            view.addTextChangedListener(newValue);
        }
    }

当我们在EditTextView中的输入发生变化时,将会回调到TextWatcher.onTextChanged(),在其中会调用textAttrChanged的onChange(),textAttrCHanged是我们在ActivityLoginBindingImpl中调用传递的InverseBindingListener对象:

#ActivityLoginBindingImpl.java
private androidx.databinding.InverseBindingListener etPasswordandroidTextAttrChanged = new androidx.databinding.InverseBindingListener() {
        @Override
        public void onChange() {
            // Inverse of viewModel.password.get()
            //         is viewModel.password.set((java.lang.String) callbackArg_0)
            java.lang.String callbackArg_0 = androidx.databinding.adapters.TextViewBindingAdapter.getTextString(etPassword);
            // localize variables for thread safety
            // viewModel.password != null
            boolean viewModelPasswordJavaLangObjectNull = false;
            // viewModel.password.get()
            java.lang.String viewModelPasswordGet = null;
            // viewModel
            com.cmnit.sign.ui.login.LoginViewModel viewModel = mViewModel;
            // viewModel.password
            androidx.databinding.ObservableField viewModelPassword = null;
            // viewModel != null
            boolean viewModelJavaLangObjectNull = false;



            viewModelJavaLangObjectNull = (viewModel) != (null);
            if (viewModelJavaLangObjectNull) {


                viewModelPassword = viewModel.password;

                viewModelPasswordJavaLangObjectNull = (viewModelPassword) != (null);
                if (viewModelPasswordJavaLangObjectNull) {




                    viewModelPassword.set(((java.lang.String) (callbackArg_0)));
                }
            }
        }
    };

第一步,获取控件中输入的值
第二步,通过ObservableField.set()将值赋值给viewModel中的viewModelPassword对象
第三步,更新数据之后,判断新旧数据是否一致,不一致的话会再更新一次UI。

实现中会比对新旧数据是否一致,不一致的情况下才进行更新,这样也避免了:设置数据 -> 触发数据变动回调 -> 更新数据 -> 再次触发数据变动回调 -> ...引起的死循环问题。

Q:Databinding的优点是什么?

  • 无需多处调用控件,原本调用的地方只需要set数据即可
  • 无需手动判空
  • 完全不用写模板代码 findViewById
  • 引入DataBinding后,原本的 UI 逻辑无需改动,只需设置终态数据

Q:Databinding中使用了什么设计模式?是如何使用的?

DataBinding中使用了观察者模式, 我们在activity中把 mUser对象传入了binding类,在每次对它进行set操作的时候都会触发notify, 之后DataBinding框架会回调execute方法, 框架通过注解拿到get方法,然后拿到和UI所对应的数据,之后结合layout中对应的标注去更新UI。

Q:Databinding中的数据变化是如何更新到UI上的?UI上的状态变化又是如何更新到数据上的?

当我们setViewModel到为ActivityMainBinding时,将会调用到requestRebind()方法,而requestRebind最终会调用到executeBindings()方法中的updateRegistration(),此方法将用于在ViewModel和View之间创建一个“桥”,也就是WeakPropertyListener,它持有MainDataBinding的一个弱引用,WeakListener中的setTarget方法被调用将绑定ViewModel到MainDataBinding,setTarget被调用的时机是updateRegistration中的registerTo()方法。从ViewModel修改isShowView变量会调用WeakPropertyListener的onPropertyChanged(),在此方法中会调用requestRebind(),最终又内聚到了ViewDataBinding中的executeBindings()来刷新View。

在设置了双向绑定的控件上,为其添加了对应的监听器,监听其变动,如EditText上设置TextWatacher。具体的设置逻辑放置到了TextViewBindingAdapter.setTextWatcher里,也就是创建了一个新的TextWatcher,将我们传进来的监听器包裹在其中。为了监听代码改动我们传入的监听器是一个androidx.databinding.InverseBindingListener对象对应TextViewBindingAdapter.setTextWatcher的第四个参数,当数据发生变化的时候,TextWatch在回调onTextChanged的最后,会通过InverseBindingListener发送通知,InverseBindingListener的实现中,会去对应的View中取得控件中最新的值,并检查*Binding类是否为空,非空的话则调用对应的方法更新数据。

Q: 以activity_main.xml谈谈Databinding的初始化。

  • 对布局文件进行预处理
    DataBinding会对根布局为的布局文件进行预处理,生成三个额外的文件(正常布局版本)activity_main.xml、(绑定布局版本)activity_main-layout.xml
    在activity_main.xml文件中,根元素和使用了binding表达式的view将被设置Tag。而在activity_main-layout.xml文件中,包含了标签、data标签 、Tag标记以及里面的 variable标签,还有各个view中的 binding表达式。
  • 生成ActivityMainBinding与BR类
    DataBinding 将会依据上面两个xml文件(即 activtiy_main.xml 和 activtiy_main-layout.xml)生成两个类,一个类是 ActivityMainBinding,它继承自 ViewDataBinding,里面包含了使用了Binding表达式的view控件变量以及我们传入布局variable字段下的变量;
    另一个类是BR类,它的内容非常简单:
    _all变量是默认生成的,user变量是对应 ActivityMainBinding类 中传入的 variable字段下的mUser变量
  • 生成ActivityMainBinding实例并绑定
    1 --Inflate处理后的布局文件,DataBindingUtil 将会 渲染activity_main.xml文件,得到一个ViewGroup变量root;
    2 --生成 ActivityMainBinding实例对象,DataBindingUtil 会将这个 变量root 传递给 ActivityMainBinding的构造方法,生成一个 ActivityMainBinding的实例。在ActivityMainBinding的构造方法中将会调用ViewDataBinding的mapBindings方法,通过对root view进行一次遍历,将view中所有的控件查找出来并根据对应的tag进行绑定,初始化相应的field,查找效率比使用findViewById更加高效。在setTagRoot()中,将 ActivityMainBinding 和布局文件中的 LinearLayout 关联起来了。
    最后调用invalidateAll()引发数据绑定。
    3 --数据绑定
    ActivityMainBinding 将会计算 各个view上的 binding表达式,然后赋值给view相应的属性。

Q:activity_main.xml、activity_main-layout.xml和ActivityMainBinding.java的关系?(data-binding编译器是怎样生成代码的?)

编译器的核心为compiler.android.databinding.annotationprocessor包下的ProcessDataBinding类。这个类的职责是一步一步执行处理列表。

  mProcessingSteps = Arrays.asList(
                new ProcessMethodAdapters(),
                new ProcessExpressions(),
                new ProcessBindable()
        );
  • 我们先看第一个处理步骤——ProcessMethodAdapters。这个类提供搜索工程中所有的类,哪一个类哪一个方法添加了下面的注解:@BindingAdapter, @Untaggable, @BindingMethods, @BindingConversion, @InverseBindingAdapter, @InverseBindingMethods。并且把它们保存在SetterStore,后面应该在executeBinding用到正如我们上面所说。在编译期间,注解处理器拿到的这些信息会被存放在setter_store.bin文件中。
  • 第二步是ProcessExpressions处理表达式。在这一步中会搜索工程中所有xml文件并且会转换最外层为标签支持data-binding的xml文件。会把这个文件拆分为2个文件正如第一部分所提到的:activity_main.xml(正常的布局文件)和activity_main-layout.xml(包含绑定信息)。LayoutBinder是最有意思的类,它使用(XmlParser中)layoutBundle在activity_main-layout.xml中计算表达式,位置和目标。
  • 第三步是ProcessBindable。这个处理生成BR类,绑定属性的id,例如:BR.text, BR.item, BR.isShowView, ...

Android DataBinding 数据绑定

DataBinding实现原理探析

(译)深入理解Data Binding原理

Android - DataBinding源码解读(内存消耗和双向绑定原理分析)

“终于懂了“系列:Jetpack AAC完整解析(五)DataBinding 重新认知!

MVVM架构篇之databinding机制解析

你可能感兴趣的:(JetPack 之 DataBinding)