DataBinding是Google发布的支持库,它可以实现UI组件及数据源的双向绑定。使用DataBinding可以轻松实现MVVM模式,当数据发生变化时会体现在View界面上,反过来界面内容变化也会同步更新到ViewModel中的数据源。同时,在Google推出的Android Jetpack组件中,也将DataBinding放在了Architecture类别之中。
一、DataBinding生成代码
启用DataBinding的方法是在对应Model的build.gradle文件里加入以下代码,同步后就能引入对 DataBinding 的支持。
android {
dataBinding {
enabled = true
}
}
DataBinding的布局文件使用了layout标签作为根节点,其中包含了data标签与view标签view标签。打开xml布局文件,根据DataBinding需要的布局规则进行编写,实例如下:
Databinding在编译时会生成代码,利用的技术是Apt(annotation-processing-tool)。在我们按照规则编写完xml之后,就会生成相应的java文件,文件名为xml文件名加上Binding后缀,按照上述实例会生成ActivityMainBinding.java文件,其生成的路径为/app/build/generated/source/apt/debug/com/lxbnjupt/databindingdemo/databinding/ActivityMainBinding。
package com.lxbnjupt.databindingdemo.databinding;
import com.lxbnjupt.databindingdemo.R;
import com.lxbnjupt.databindingdemo.BR;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.View;
@SuppressWarnings("unchecked")
public class ActivityMainBinding extends android.databinding.ViewDataBinding {
@Nullable
private static final android.databinding.ViewDataBinding.IncludedLayouts sIncludes;
@Nullable
private static final android.util.SparseIntArray sViewsWithIds;
static {
sIncludes = null;
sViewsWithIds = null;
}
// views
@NonNull
private final android.widget.LinearLayout mboundView0;
@NonNull
public final android.widget.TextView tvName;
// variables
@Nullable
private com.lxbnjupt.databindingdemo.UserViewModel mViewmodel;
// values
// listeners
// Inverse Binding Event Handlers
public ActivityMainBinding(@NonNull android.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
super(bindingComponent, root, 1);
final Object[] bindings = mapBindings(bindingComponent, root, 2, sIncludes, sViewsWithIds);
this.mboundView0 = (android.widget.LinearLayout) bindings[0];
this.mboundView0.setTag(null);
this.tvName = (android.widget.TextView) bindings[1];
this.tvName.setTag(null);
setRootTag(root);
// listeners
invalidateAll();
}
@Override
public void invalidateAll() {
synchronized(this) {
mDirtyFlags = 0x4L;
}
requestRebind();
}
@Override
public boolean hasPendingBindings() {
synchronized(this) {
if (mDirtyFlags != 0) {
return true;
}
}
return false;
}
@Override
public boolean setVariable(int variableId, @Nullable Object variable) {
boolean variableSet = true;
if (BR.viewmodel == variableId) {
setViewmodel((com.lxbnjupt.databindingdemo.UserViewModel) variable);
}
else {
variableSet = false;
}
return variableSet;
}
public void setViewmodel(@Nullable com.lxbnjupt.databindingdemo.UserViewModel Viewmodel) {
this.mViewmodel = Viewmodel;
synchronized(this) {
mDirtyFlags |= 0x2L;
}
notifyPropertyChanged(BR.viewmodel);
super.requestRebind();
}
@Nullable
public com.lxbnjupt.databindingdemo.UserViewModel getViewmodel() {
return mViewmodel;
}
@Override
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
switch (localFieldId) {
case 0 :
return onChangeViewmodelName((android.databinding.ObservableField) object, fieldId);
}
return false;
}
private boolean onChangeViewmodelName(android.databinding.ObservableField ViewmodelName, int fieldId) {
if (fieldId == BR._all) {
synchronized(this) {
mDirtyFlags |= 0x1L;
}
return true;
}
return false;
}
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
com.lxbnjupt.databindingdemo.UserViewModel viewmodel = mViewmodel;
java.lang.String viewmodelNameGet = null;
android.databinding.ObservableField viewmodelName = null;
if ((dirtyFlags & 0x7L) != 0) {
if (viewmodel != null) {
// read viewmodel.name
viewmodelName = viewmodel.name;
}
updateRegistration(0, viewmodelName);
if (viewmodelName != null) {
// read viewmodel.name.get()
viewmodelNameGet = viewmodelName.get();
}
}
// batch finished
if ((dirtyFlags & 0x7L) != 0) {
// api target 1
android.databinding.adapters.TextViewBindingAdapter.setText(this.tvName, viewmodelNameGet);
}
}
// Listener Stub Implementations
// callback impls
// dirty flag
private long mDirtyFlags = 0xffffffffffffffffL;
@NonNull
public static ActivityMainBinding inflate(@NonNull android.view.LayoutInflater inflater, @Nullable android.view.ViewGroup root, boolean attachToRoot) {
return inflate(inflater, root, attachToRoot, android.databinding.DataBindingUtil.getDefaultComponent());
}
@NonNull
public static ActivityMainBinding inflate(@NonNull android.view.LayoutInflater inflater, @Nullable android.view.ViewGroup root, boolean attachToRoot, @Nullable android.databinding.DataBindingComponent bindingComponent) {
return android.databinding.DataBindingUtil.inflate(inflater, com.lxbnjupt.databindingdemo.R.layout.activity_main, root, attachToRoot, bindingComponent);
}
@NonNull
public static ActivityMainBinding inflate(@NonNull android.view.LayoutInflater inflater) {
return inflate(inflater, android.databinding.DataBindingUtil.getDefaultComponent());
}
@NonNull
public static ActivityMainBinding inflate(@NonNull android.view.LayoutInflater inflater, @Nullable android.databinding.DataBindingComponent bindingComponent) {
return bind(inflater.inflate(com.lxbnjupt.databindingdemo.R.layout.activity_main, null, false), bindingComponent);
}
@NonNull
public static ActivityMainBinding bind(@NonNull android.view.View view) {
return bind(view, android.databinding.DataBindingUtil.getDefaultComponent());
}
@NonNull
public static ActivityMainBinding bind(@NonNull android.view.View view, @Nullable android.databinding.DataBindingComponent bindingComponent) {
if (!"layout/activity_main_0".equals(view.getTag())) {
throw new RuntimeException("view tag isn't correct on view:" + view.getTag());
}
return new ActivityMainBinding(bindingComponent, view);
}
/* flag mapping
flag 0 (0x1L): viewmodel.name
flag 1 (0x2L): viewmodel
flag 2 (0x3L): null
flag mapping end*/
//end
}
二、DataBinding如何避免findViewById
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
}
}
在初始化时,我们利用DataBindingUtil.setContentView()方法来建立layout与ViewDataBinding之间的关系,我们具体看一下该方法的实现。
public static T setContentView(Activity activity, int layoutId) {
return setContentView(activity, layoutId, sDefaultComponent);
}
public static T setContentView(Activity activity, int layoutId,
DataBindingComponent bindingComponent) {
activity.setContentView(layoutId);
View decorView = activity.getWindow().getDecorView();
ViewGroup contentView = (ViewGroup) decorView.findViewById(android.R.id.content);
return bindToAddedViews(bindingComponent, contentView, 0, layoutId);
}
由源码可知,该方法先是调用了activity.setContentView(layoutId),这个与我们一般在Activity中的使用方式完全一致。接着获取Activity的DecorView,然后再将id为android.R.id.content的view传入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);
}
}
static T bind(DataBindingComponent bindingComponent, View root,
int layoutId) {
return (T) sMapper.getDataBinder(bindingComponent, root, layoutId);
}
获取到layout中的根布局,并且调用bind()方法。而bind()方法内调用DataBinderMapper的getDataBinder方法。DataBinderMapper也是编译时生成的类,主要是建立layout与ViewDataBinding之间的映射,其路径为/app/build/generated/source/apt/debug/android/databinding/DataBinderMapper.java。
package android.databinding;
import com.lxbnjupt.databindingdemo.BR;
class DataBinderMapper {
final static int TARGET_MIN_SDK = 18;
public DataBinderMapper() {
}
public android.databinding.ViewDataBinding getDataBinder(android.databinding.DataBindingComponent bindingComponent, android.view.View view, int layoutId) {
switch(layoutId) {
case com.lxbnjupt.databindingdemo.R.layout.activity_main:
return com.lxbnjupt.databindingdemo.databinding.ActivityMainBinding.bind(view, bindingComponent);
}
return null;
}
android.databinding.ViewDataBinding getDataBinder(android.databinding.DataBindingComponent bindingComponent, android.view.View[] views, int layoutId) {
switch(layoutId) {
}
return null;
}
... ...
}
由源码可知,根据layoutId会去调用对应ViewDataBinding的bind()方法。回看一下上一部分中ActivityMainBinding代码,我们可以知道最终会去调用ActivityMainBinding的构造方法,具体如下:
public ActivityMainBinding(@NonNull android.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
super(bindingComponent, root, 1);
final Object[] bindings = mapBindings(bindingComponent, root, 2, sIncludes, sViewsWithIds);
this.mboundView0 = (android.widget.LinearLayout) bindings[0];
this.mboundView0.setTag(null);
this.tvName = (android.widget.TextView) bindings[1];
this.tvName.setTag(null);
setRootTag(root);
// listeners
invalidateAll();
}
我们重点看一下mapBindings()这个方法:
protected static Object[] mapBindings(DataBindingComponent bindingComponent, View root,
int numBindings, IncludedLayouts includes, SparseIntArray viewsWithIds) {
Object[] bindings = new Object[numBindings];
mapBindings(bindingComponent, root, bindings, includes, viewsWithIds, true);
return bindings;
}
private static void mapBindings(DataBindingComponent bindingComponent, View view,
Object[] bindings, IncludedLayouts includes, SparseIntArray viewsWithIds,
boolean isRoot) {
final int indexInIncludes;
final ViewDataBinding existingBinding = getBinding(view);
if (existingBinding != null) {
return;
}
Object objTag = view.getTag();
final String tag = (objTag instanceof String) ? (String) objTag : null;
boolean isBound = false;
if (isRoot && tag != null && tag.startsWith("layout")) {
final int underscoreIndex = tag.lastIndexOf('_');
if (underscoreIndex > 0 && isNumeric(tag, underscoreIndex + 1)) {
final int index = parseTagInt(tag, underscoreIndex + 1);
if (bindings[index] == null) {
bindings[index] = view;
}
indexInIncludes = includes == null ? -1 : index;
isBound = true;
} else {
indexInIncludes = -1;
}
} else if (tag != null && tag.startsWith(BINDING_TAG_PREFIX)) {
int tagIndex = parseTagInt(tag, BINDING_NUMBER_START);
if (bindings[tagIndex] == null) {
bindings[tagIndex] = view;
}
isBound = true;
indexInIncludes = includes == null ? -1 : tagIndex;
} else {
// Not a bound view
indexInIncludes = -1;
}
if (!isBound) {
final int id = view.getId();
if (id > 0) {
int index;
if (viewsWithIds != null && (index = viewsWithIds.get(id, -1)) >= 0 &&
bindings[index] == null) {
bindings[index] = view;
}
}
}
if (view instanceof ViewGroup) {
final ViewGroup viewGroup = (ViewGroup) view;
final int count = viewGroup.getChildCount();
int minInclude = 0;
for (int i = 0; i < count; i++) {
final View child = viewGroup.getChildAt(i);
boolean isInclude = false;
if (indexInIncludes >= 0 && child.getTag() instanceof String) {
String childTag = (String) child.getTag();
if (childTag.endsWith("_0") &&
childTag.startsWith("layout") && childTag.indexOf('/') > 0) {
// This *could* be an include. Test against the expected includes.
int includeIndex = findIncludeIndex(childTag, minInclude,
includes, indexInIncludes);
if (includeIndex >= 0) {
isInclude = true;
minInclude = includeIndex + 1;
final int index = includes.indexes[indexInIncludes][includeIndex];
final int layoutId = includes.layoutIds[indexInIncludes][includeIndex];
int lastMatchingIndex = findLastMatching(viewGroup, i);
if (lastMatchingIndex == i) {
bindings[index] = DataBindingUtil.bind(bindingComponent, child,
layoutId);
} else {
final int includeCount = lastMatchingIndex - i + 1;
final View[] included = new View[includeCount];
for (int j = 0; j < includeCount; j++) {
included[j] = viewGroup.getChildAt(i + j);
}
bindings[index] = DataBindingUtil.bind(bindingComponent, included,
layoutId);
i += includeCount - 1;
}
}
}
}
if (!isInclude) {
mapBindings(bindingComponent, child, bindings, includes, viewsWithIds, false);
}
}
}
}
通过该方法,递归得到最后的bindings数组。如果设置了id的,就将view变量设置为public,这样就避免了findViewById的代码。这种方式从性能上比findViewById更加高效,因为DataBinding只需要遍历一次view的数量,而findViewById多次调用会遍历多次。
三、View与DataBinding之间的双向绑定
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
}
}
我们在Activity中获取了ActivityMainBinding实例,那么很明显View就持有了ViewDataBinding的实例。
通过前面DataBinding如何避免findViewById的源码分析,我们知道在ViewDataBinding的构造方法中会使用mapBindings()方法获取bindings数组,继而通过bindings数组就可以得到各个View,也就是说ViewDataBinding里持有各个View的引用。
至此,View与DataBinding之间的双向绑定关系就建立起来了。
四、ViewModel与DataBinding之间的双向绑定
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
UserViewModel model = new UserViewModel();
binding.setViewmodel(model);
}
}
我们在Activity中创建了ViewModel的实例,并将它设置到ViewDataBinding中去:
// ActivityMainBinding # setViewmodel
public void setViewmodel(@Nullable com.lxbnjupt.databindingdemo.UserViewModel Viewmodel) {
this.mViewmodel = Viewmodel;
synchronized(this) {
mDirtyFlags |= 0x2L;
}
notifyPropertyChanged(BR.viewmodel);
super.requestRebind();
}
由编译时生成的ActivityMainBinding代码可知,ActivityMainBinding将设置进来的ViewModel赋值给了自己的成员变量,也就意味着ActivityMainBinding中持有了ViewModel。
那么,ViewModel是如何持有ViewDataBinding的呢,这个问题的分析比较复杂。我们看到上面调用的setViewmodel()方法,最后会去调用super.requestRebind()方法。
protected void requestRebind() {
if (mContainingBinding != null) {
mContainingBinding.requestRebind();
} else {
synchronized (this) {
if (mPendingRebind) {
return;
}
mPendingRebind = true;
}
if (USE_CHOREOGRAPHER) {
mChoreographer.postFrameCallback(mFrameCallback);
} else {
mUIThreadHandler.post(mRebindRunnable);
}
}
}
这个方法最后会辗转调用到ActivityMainBinding的executeBindings()方法。
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
com.lxbnjupt.databindingdemo.UserViewModel viewmodel = mViewmodel;
java.lang.String viewmodelNameGet = null;
android.databinding.ObservableField viewmodelName = null;
if ((dirtyFlags & 0x7L) != 0) {
if (viewmodel != null) {
// read viewmodel.name
viewmodelName = viewmodel.name;
}
updateRegistration(0, viewmodelName);
if (viewmodelName != null) {
// read viewmodel.name.get()
viewmodelNameGet = viewmodelName.get();
}
}
// batch finished
if ((dirtyFlags & 0x7L) != 0) {
// api target 1
android.databinding.adapters.TextViewBindingAdapter.setText(this.tvName, viewmodelNameGet);
}
}
我们注意到在这个方法中创建了一个ObservableField对象(即viewmodelName),并将它赋值为ViewModel中的对应对象,之后将它作为参数去调用updateRegistration()方法。
protected boolean updateRegistration(int localFieldId, Observable observable) {
return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
}
我们先看一下这个CREATE_PROPERTY_LISTENER,它是一个CreateWeakListener对象:
private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
@Override
public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();
}
};
之后调用了updateRegistration的重载方法,在这个重载方法中又会去调用registerTo()方法:
private boolean updateRegistration(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
if (observable == null) {
return unregisterFrom(localFieldId);
}
WeakListener listener = mLocalFieldObservers[localFieldId];
if (listener == null) {
registerTo(localFieldId, observable, listenerCreator);
return true;
}
if (listener.getTarget() == observable) {
return false;//nothing to do, same object
}
unregisterFrom(localFieldId);
registerTo(localFieldId, observable, listenerCreator);
return true;
}
protected void registerTo(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
if (observable == null) {
return;
}
WeakListener listener = mLocalFieldObservers[localFieldId];
if (listener == null) {
listener = listenerCreator.create(this, localFieldId);
mLocalFieldObservers[localFieldId] = listener;
}
listener.setTarget(observable);
}
由源码可知,在registerTo()方法中会调用listenerCreator.create()方法去创建一个WeakListener对象。而这个方法最终会调用上面所提到的CREATE_PROPERTY_LISTENER对象的create()方法。最终创建了一个WeakPropertyListener对象,并调用它的getListener()方法获取WeakListener对象。
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 setTarget(T object) {
unregister();
mTarget = object;
if (mTarget != null) {
mObservable.addListener(mTarget);
}
}
public boolean unregister() {
boolean unregistered = false;
if (mTarget != null) {
mObservable.removeListener(mTarget);
unregistered = true;
}
mTarget = null;
return unregistered;
}
public T getTarget() {
return mTarget;
}
protected ViewDataBinding getBinder() {
ViewDataBinding binder = get();
if (binder == null) {
unregister(); // The binder is dead
}
return binder;
}
}
我们再回到之前的registerTo()方法,该方法中在获取到WeakListener对象之后,便调用了它的setTarget()方法。而在创建WeakListener对象时,其构造方法中的ObservableReference
@Override
public void addListener(Observable target) {
target.addOnPropertyChangedCallback(this);
}
@Override
public void onPropertyChanged(Observable sender, int propertyId) {
ViewDataBinding binder = mListener.getBinder();
if (binder == null) {
return;
}
Observable obj = mListener.getTarget();
if (obj != sender) {
return; // notification from the wrong object?
}
binder.handleFieldChange(mListener.mLocalFieldId, sender, propertyId);
}
我们知道addListener()方法中的target就是ViewModel中的ObservableField对象,它是一个被观察者。通过addOnPropertyChangedCallback()方法,相当于向target注册一个观察者,在数据源有变化的时候,回调onPropertyChanged()方法。在onPropertyChanged()方法中,又通过WeakListener对象获取ViewDataBinding对象,之后再通过ViewDataBinding对象的handleFieldChange()方法去更新View。通过以上分析,我们可以知道其实ViewModel持有WeakPropertyListener,WeakPropertyListener又持有WeakListener,而WeakListener又持有ViewDataBinding对象(弱引用方式),也就是ViewModel间接地持有了ViewDataBinding。
至此,ViewModel与DataBinding之间的双向绑定关系就建立起来了。