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的绑定。控件中可以使用@{}进行绑定,@{}为单向绑定@={}为双向绑定
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<!--DataBinding的配置文件-->
<data>
<!--name属性相当于声明了一个全局属性、type指定name对应的具体的数据类或是MVVM中VM-->
<variable
name="userInfo"
type="com.scjd.framemodel_databinding.UserInfo"/>
</data>
<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"
android:padding="50dp"
android:orientation="vertical">
<EditText android:layout_width="match_parent" android:layout_height="wrap_content"
android:text="@={userInfo.userName}"/>
<EditText android:layout_width="match_parent" android:layout_height="wrap_content"
android:text="@{userInfo.passWord}"/>
<View android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</LinearLayout>
</layout>
3.声明一个数据类,使用ObservableField声明属性并通过范型指定数据类型。
public class UserInfo {
public ObservableField<String> userName = new ObservableField<>();
public ObservableField<String> 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.xml
和activity_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
中的内容:
...
<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent"
android:tag="layout/activity_main_0" />
...
然后DataBinding
把最初布局文件中的<data>
以及各个view
中的 binding
表达式 内容抽取出来,生成了 activtiy_main-layout.xml
文件,其内容类似如下代码:
<Layout layout="activity_main" absoluteFilePath="/home/framgia/android/app/src/main/res/layout/activity_main.xml"
directory="layout" isMerge="false" modulePackage="com.harpacrista">
<Variables declared="true" name="viewModel" type="com.example.main.MainViewModel">
<location endLine="8" endOffset="51" startLine="6" startOffset="8" />
</Variables>
<Imports name="View" type="android.view.View">
<location endLine="10" endOffset="42" startLine="10" startOffset="8" />
</Imports>
xml
<Targets>
<Target tag="layout/activity_main_0" view="TextView">
<Expressions>
<Expression attribute="android:text" text=" viewModel.text ">
<Location endLine="16" endOffset="41" startLine="16" startOffset="8" />
<TwoWay>false</TwoWay>
<ValueLocation endLine="16" endOffset="39" startLine="16" startOffset="24" />
</Expression>
</Expressions>
<location endLine="16" endOffset="44" startLine="14" startOffset="4" />
</Target>
</Targets>
</Layout>
在<Layout>
标签的里面,毫无疑问,有<Variables>
标签和<Imports>
标签。它们也包含位置location
信息。<Target>
标签告诉ViewModel
应该映射到哪个View
,绑定类型为单向还是双向,View
的位置及View
值的位置。
我们有一个<Target>
标签的列表。在布局文件中我们可以有多个View/ViewGroup
,但只有包含data-binding
表达式的View/ViewGroup
才会出现在这里。<Target>
标签里面的<Expressions>
标签表示View
的data-binding
表达式。
接着,DataBinding
还生成了ActivityMainBinding.java
和ActivityMainBindingImpl.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 extends ViewDataBinding> T setContentView(@NonNull Activity activity,
int layoutId, @Nullable DataBindingComponent bindingComponent) {
activity.setContentView(layoutId);
View decorView = activity.getWindow().getDecorView();
ViewGroup contentView = (ViewGroup) decorView.findViewById(android.R.id.content);
return bindToAddedViews(bindingComponent, contentView, 0, layoutId);
}
方法中通过activity的setContentView
加载布局,并通过window
找到id为content
的ViewGroup
,它是一个FrameLayout
用于加载我们添加的布局文件。接下来就是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);
}
}
parent中的子View就是我们布局文件中的根布局LinearLayout,所以走的是if中的代码
private static DataBinderMapper sMapper = new DataBinderMapperImpl();
static <T extends ViewDataBinding> 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<java.lang.String> 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<java.lang.String> 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上;
- 将数据模型上的值更新到UI上,如将firstName设置到mboundView1上,lastName设置到mboundView2上。可以看到,每一个<variable>标签声明的变量都有一个专属的标记位,当改变量的值被更新时,对应的脏标记位就会置为1,executeBindings的时候变回将这些变动更新到对应的控件。
- 在设置了双向绑定的控件上,为其添加对应的监听器,监听其变动,如: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<String> 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<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);
}
}
WeakPropertyListener内有持有变量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 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
维持了从localFieldId
到WeakListener
的映射。先从mLocalFieldObservers
取localFieldId
对应的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
中。然后调用WeakListener
的setTarget()
。
listener是WeakListener对象,查看WeakListener.setTarger:
#WeakListener.java
private final ObservableReference<T> mObservable;
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);
}
}
mTager
是被观察者,在我们例子中是updateRegistration
中传进来的viewModelLoginErrorTip
,也就是viewModel
中定义的ObservableField
对象
mObservable
是WeakPropertyListener
对象,是构建WeakListener
时传进来的,WeakListener
构造函数是WeakPropertyListener
构造函数中传进来的
public WeakPropertyListener(ViewDataBinding binder, int localFieldId) {
mListener = new WeakListener<Observable>(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
中。然后调用WeakListener
的setTarget()
。
查看WeakPropertyListener.addListener
#WeakPropertyListener.java
@Override
public void addListener(Observable target) {
target.addOnPropertyChangedCallback(this);
}
target
是ObservableFiled
。this
是WeakPropertyListener
。
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<Observable.OnPropertyChangedCallback, Observable, Void>
,其内部有一个List<WeakPropertyListener> mCallbacks
。WeakPropertyListener
继承了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<Observable.OnPropertyChangedCallback, Observable, Void> NOTIFIER_CALLBACK = new CallbackRegistry.NotifierCallback<Observable.OnPropertyChangedCallback, Observable, Void>() {
@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<Observable> {
final WeakListener<Observable> 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<java.lang.String>) object, fieldId);
...
}
private boolean onChangeViewModelLoginErrorTip(androidx.databinding.ObservableField<java.lang.String> 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<String> 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<T> implements CustomObservable{
private List<WeakPropertyListener> 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<WeakPropertyListener> 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<java.lang.String> 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会对根布局为<layout>的布局文件进行预处理,生成三个额外的文件(正常布局版本)activity_main.xml、(绑定布局版本)activity_main-layout.xml
在activity_main.xml文件中,根元素和使用了binding表达式的view将被设置Tag。而在activity_main-layout.xml文件中,包含了<layout>标签、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文件并且会转换最外层为<layout></layout>标签支持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源码解读(内存消耗和双向绑定原理分析)