DataBinding是谷歌15年推出的library。DataBinding支持双向绑定,能大大减少绑定app逻辑与layout文件的“胶水代码”,例如setText、findViewById等代码。双向绑定,指的是将数据与界面绑定起来,当数据发生变化时会体现在界面上,反过来界面内容变化也会同步更新到数据上,使用DataBinding能轻松实现MVVM模式。本文在分析DataBinding时,将用ViewModel(VM)表示数据,用View(V)表示界面。
本文着重于对DataBinding原理的分析,因此关于MVVM、DataBinding使用过程不做介绍。推荐一些相关的文章:
- MVC vs. MVP vs. MVVM on Android(英)
- android-architecture-todo-mvvm-databinding(DataBinding实现MVVM谷歌示例))
- Data Binding官方文档(英)
- 精通 Android Data Binding
- Data Binding高级篇(补充双向绑定)
在分析双向绑定的原理之前,先了解DataBinding生成的主要代码,有activity_main.xml、activity_main-layout.xml、ActivityMainBinding.java、BR.java、DataBinderMapper.java等等。
位置:
app/build/intermediates/data-binding-layout-out/debug/layout/activity_main.xml
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent" android:tag="layout/activity_main_0" xmlns:android="http://schemas.android.com/apk/res/android">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:tag="binding_1" />
RelativeLayout>
可以看出,activity_main.xml与不用DataBinding前的布局文件差不多,唯一的区别是加了tag标记。 根布局的tag为layout/activity_main_0,而button的tag为binding_0。那我们使用DataBinding时的data标签以及里面的import与variable标签在哪里?其实它们都在下面介绍得activity_main-layout.xml里面。使用DataBinding重写后的布局文件无法被“理解”,因此需要经过DataBinding处理成正常的布局文件activity_main.xml与包含绑定信息的文件activity_main-layout.xml。
位置:
app/build/intermediates/data-binding-info/debug/activity_main-layout.xml
<Layout layout="activity_main" modulePackage="com.example.databindingdemo" absoluteFilePath="/Users/linhaiyang807/Desktop/studioWorkspace/DataBindingDemo/app/src/main/res/layout/activity_main.xml" directory="layout" isMerge="false">
<Variables declared="true" type="User" name="user">
<location startLine="7" startOffset="8" endLine="9" endOffset="25"/>
Variables>
<Imports type="com.example.databindingdemo.User" name="User">
<location startLine="5" startOffset="8" endLine="5" endOffset="57"/>
Imports>CREATE_PROPERTY_LISTENER
<Targets>
<Target tag="layout/activity_main_0" view="RelativeLayout">
<Expressions/>
<location startLine="12" startOffset="4" endLine="22" endOffset="20"/>
Target>
<Target id="@+id/button" tag="binding_1" view="Button">
<Expressions>
<Expression text="user.name" attribute="android:text">
<Location startLine="20" startOffset="12" endLine="20" endOffset="38"/>
<TwoWay>falseTwoWay>
<ValueLocation startLine="20" startOffset="28" endLine="20" endOffset="36"/>
Expression>
Expressions>
<location startLine="16" startOffset="8" endLine="20" endOffset="41"/>
Target>
Targets>
Layout>
activity_main-layout.xml内包含了数据绑定相关的信息。Variables与Imports和布局文件中的variable与import标签对应,Targets标签则告诉VM变化时对应的V、绑定关系是单向还是双向的以及DataBinding的表达式Expressions。
位置:
app/build/generated/source/apt/debug/com/example/databindingdemo/databinding/ActivityMainBinding.java
public class ActivityMainBinding extends android.databinding.ViewDataBinding {
private static final android.databinding.ViewDataBinding.IncludedLayouts sIncludes;
private static final android.util.SparseIntArray sViewsWithIds;
static {
sIncludes = null;
sViewsWithIds = null;
}
// views
public final android.widget.Button button;
private final android.widget.RelativeLayout mboundView0;
// variables
private com.example.databindingdemo.User mUser;
// values
// ……
}
可以看到ActivityMainBinding里面持有了一些view的变量,那是不是布局中所有的view都会在ActivityMainBinding中?其实不是,ActivityMainBinding里的view变量有三种类型,① 根布局 ② 含有@{}绑定的view ③ 含有id属性的view。 设置了id的view对应的变量为public的,可以通过ActivityMainBinding直接访问,避免了findViewById的过程,具体原理将在下面分析。ActivityMainBinding内部主要负责双向绑定的功能,留在下面分析。
位置:
app/build/generated/source/apt/debug/com/android/databinding/library/baseAdapters/BR.java
public class BR {
public static final int _all = 0;
public static final int user = 1;
}
BR文件存储了绑VM的id,功能与R文件类似。
位置:
app/build/generated/source/apt/debug/android/databinding/DataBinderMapper.java
class DataBinderMapper {
final static int TARGET_MIN_SDK = 21;
public DataBinderMapper() {
}
public android.databinding.ViewDataBinding getDataBinder(android.databinding.DataBindingComponent bindingComponent, android.view.View view, int layoutId) {
switch(layoutId) {
case com.example.databindingdemo.R.layout.activity_main:
return com.example.databindingdemo.databinding.ActivityMainBinding.bind(view, bindingComponent);
}
return null;
}
// ……
}
DataBinderMapper主要提供了从布局文件layoutId到ViewBinding类的映射。
在初始化时我们会调用下面的代码:
final ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.setUser(user);
进入setUser:
public void setUser(User User) {
this.mUser = User;
synchronized(this) {
this.mDirtyFlags |= 2L;
}
this.notifyPropertyChanged(1);
super.requestRebind();
}
mDirtyFlags用于表示哪个属性发生变化,notifyPropertyChanged(1)实则为notifyPropertyChanged(BR.user),顾名思义,是发出user数据变化的通知。看看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);
}
}
}
如果之前不是处于mPengdingRebind状态,则标示mPendingRebind为true。然后根据api版本做了点不同的处理,16及以上的,会往mChoreographer发一个mFrameCallback;否则直接往UI线程发一个mRebindRunnable。其实这里俩个分支的结果基本一致,mChoreographer会在界面刷新时执行mRebindRunnable,Choreographer是api16后引入的用于解决UI卡顿的,当收到VSYNC(定时中断)时,在doFrame里去执行相应的操作。
- Android Choreographer和VSYNC机制
看看mRebindRunnable发生了什么:
/**
* Runnable executed on animation heartbeat to rebind the dirty Views.
*/
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();
}
};
重置mPendingRebind为false。如果api19及以上时,判断rootView是否attach到window上,如果没有的话,则对这个attach的状态进行监听。最终都会执行executePendingBindings(),继而调用executeBindingsInternal()方法。
/**
* Evaluates the pending bindings without executing the parent bindings.
*/
private void executeBindingsInternal() {
if (mIsExecutingPendingBindings) {
requestRebind();
return;
}
if (!hasPendingBindings()) {
return;
}
mIsExecutingPendingBindings = true;
mRebindHalted = false;
if (mRebindCallbacks != null) {
mRebindCallbacks.notifyCallbacks(this, REBIND, null);
// The onRebindListeners will change mPendingHalted
if (mRebindHalted) {
mRebindCallbacks.notifyCallbacks(this, HALTED, null);
}
}
if (!mRebindHalted) {
executeBindings();
if (mRebindCallbacks != null) {
mRebindCallbacks.notifyCallbacks(this, REBOUND, null);
}
}
mIsExecutingPendingBindings = false;
}
如果mIsExecutingPendingBindings为true,表明当前已经在执行这段代码了,则调用一次requestRebind()。如果当前没有需要需要绑定的数据(hasPendingBindings为false),则返回不处理。
接下来通知所有的监听(RebindCallback)一个REBIND的事件(可以理解为即将绑定)。在监听里可以把mRebindHalted置为true,以终止后面的executeBindings()方法。如果被终止了,同样HALTED的事件(可以理解为终止)也会通知给所有的监听。如果没被终止,则会调用executeBindings()进行绑定,会发REBOUND的事件(可以理解为正在绑定)给所有监听。
那RebindCallback如何设置mRebindHalted呢?RebindCallback可以通过ActivityMainBinding.addOnRebindCallback设置。我们看看mRebindCallbacks长什么样:
/**
* The collection of OnRebindCallbacks.
*/
private CallbackRegistry mRebindCallbacks;
/**
* Add a listener to be called when reevaluating dirty fields. This also allows automatic
* updates to be halted, but does not stop explicit calls to {@link #executePendingBindings()}.
*
* @param listener The listener to add.
*/
public void addOnRebindCallback(OnRebindCallback listener) {
if (mRebindCallbacks == null) {
mRebindCallbacks = new CallbackRegistry(REBIND_NOTIFIER);
}
mRebindCallbacks.add(listener);
}
在构造mRebindCallbacks会传入一个REBIND_NOTIFIER,如下面代码所示,在进行rebind操作时,如果callback.onPreBind返回false,则会将sender.mRebindHalted置为true。这样一来executeBindings就不会被执行了。
private static final CallbackRegistry.NotifierCallback
REBIND_NOTIFIER = new NotifierCallback() {
@Override
public void onNotifyCallback(OnRebindCallback callback, ViewDataBinding sender, int mode,
Void arg2) {
switch (mode) {
case REBIND:
if (!callback.onPreBind(sender)) {
sender.mRebindHalted = true;
}
break;
case HALTED:
callback.onCanceled(sender);
break;
case REBOUND:
callback.onBound(sender);
break;
}
}
};
如果mRebindHalted没有被置为false,就是执行executeBindings方法。executeBindings是一个抽象的方法,具体实现在编译时生成的ActivityMainBinding里。
这样一来我们完成了下面这个过程:
接下来看看生成的executeBindings代码里做了什么?
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
android.databinding.ObservableField userName = null;
java.lang.String userNameGet = null;
com.example.databindingdemo.User user = mUser;
if ((dirtyFlags & 0x7L) != 0) {
if (user != null) {
// read user.name
userName = user.getName();
}
updateRegistration(0, userName);
if (userName != null) {
// read user.name.get()
userNameGet = userName.get();
}
}
// batch finished
if ((dirtyFlags & 0x7L) != 0) {
// api target 1
android.databinding.adapters.TextViewBindingAdapter.setText(this.button, userNameGet);
}
}
这里面的代码比较简单,除了对界面进行赋值,还调用了updateRegistration方法。第一个入参localFieldId与第二个入参的userName一一对应。
protected boolean updateRegistration(int localFieldId, Observable observable) {
return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
}
updateRegistration第三个参数传了CREATE_PROPERTY_LISTENER,我们先看看CREATE_PROPERTY_LISTENER是什么,再分析updateRegistration方法。
/**
* Method object extracted out to attach a listener to a bound Observable object.
*/
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 mListener;
public WeakPropertyListener(ViewDataBinding binder, int localFieldId) {
mListener = new WeakListener(binder, localFieldId, 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);
}
}
private static class WeakListener<T> extends WeakReference<ViewDataBinding> {
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;
}
// ……
}
从上面知道CREATE_PROPERTY_LISTENER是一个CreateWeakListener对象,CreateWeakListener.create()能得到WeakPropertyListener,WeakPropertyListener内有变量WeakListener,WeakListener是个弱引用,持有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方法:
private static class WeakListener<T> extends WeakReference<ViewDataBinding> {
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);
}
}
// ......
}
然后调用mObservable的addListener方法,mObservable就是在构造WeakListener传入的第三个参数。根据下面的代码,WeakPropertyListener在构造函数里构造WeakListener是传入的是this,因此mObservable其实就是WeakPropertyListener。
private static class WeakPropertyListener extends Observable.OnPropertyChangedCallback
implements ObservableReference<Observable> {
final WeakListener mListener;
public WeakPropertyListener(ViewDataBinding binder, int localFieldId) {
mListener = new WeakListener(binder, localFieldId, this);
}
@Override
public void addListener(Observable target) {
target.addOnPropertyChangedCallback(this);
}
// ......
}
在addListener里,调用Observable的addOnPropertyChangedCallback,参数为this。BaseObservable实现了接口Observable,在addOnPropertyChangedCallback里,将WeakPropertyListener加入到了mCallbacks(PropertyChangeRegistry)里面。
@Override
public void addOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
synchronized (this) {
if (mCallbacks == null) {
mCallbacks = new PropertyChangeRegistry();
}
}
mCallbacks.add(callback);
}
可以看到这个图与MVVM的架构图有点类似。
这样一来V和VM的联系就通过ViewDatabinding建立起来了。V(Activity)内有ViewDatabinding,而ViewDatabinding里持有各个V(布局中的元素)的引用。ViewDataBinding有VM的变量,而VM内的PropertyChangeRegistry监听实则为WeakPropertyListener,WeakPropertyListener得到的WeakListener能获取到ViewDatabinding的引用。
前面我们已经讲过了V和VM是如何建立起联系的,接下来我们跟着流程看看具体如何?
我们知道,如果要达到VM变化时自动绑定到View上,有下面俩种方式:
- 继承自BaseObservable,在getter上增加@Bindable注解,在setter里增加代码notifyPropertyChanged(BR.xxx)。
- 无需继承,需要将属性替换为Observable类,例如ObservableInt、ObservableField等。
这两种本质上都是一样的。在第二种方式中,当属性发生变化时,会调用notifyChange,而notifyChange与notifyPropertyChanged做的事情都是一样的,都是调用mCallbacks.notifyCallbacks去通知。
/**
* Notifies listeners that all properties of this instance have changed.
*/
public void notifyChange() {
synchronized (this) {
if (mCallbacks == null) {
return;
}
}
mCallbacks.notifyCallbacks(this, 0, null);
}
/**
* Notifies listeners that a specific property has changed. The getter for the property
* that changes should be marked with {@link Bindable} to generate a field in
* BR
to be used as fieldId
.
*
* @param fieldId The generated BR id for the Bindable field.
*/
public void notifyPropertyChanged(int fieldId) {
synchronized (this) {
if (mCallbacks == null) {
return;
}
}
mCallbacks.notifyCallbacks(this, fieldId, null);
}
mCallbacks添加监听的过程上面我们已经分析过了,也可以通过函数调用,find usages,一层层往上寻找。addOnPropertyChangedCallback(BaseObservable) -> addListener(WeakPropertyListener) -> setTarget(WeakListener)。到这里,就和上面的registerTo方法“会师”了,因此在调用binding.setUser(user)绑定时,就在Observable上加了监听,观察者就是WeakPropertyListener。当VM发生变化时,会通过mCallbacks.notifyCallbacks将变化发送出去。
那当VM发生变化时,notifyCallbacks又是如何将变化发送出去呢?
我们一层层往下跟踪,notifyCallbacks(CallbackRegistry) -> notifyRecurse(CallbackRegistry) -> notifyRemainder(CallbackRegistry) -> notifyFirst64(CallbackRegistry),最后到达下面这个方法:
/**
* Notify callbacks from startIndex to endIndex, using bits as the bit status
* for whether they have been removed or not. bits should be from mRemainderRemoved or
* mFirst64Removed. bits set to 0 indicates that all callbacks from startIndex to
* endIndex should be notified.
*
* @param sender The originator. This is an opaque parameter passed to
* {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
* @param arg An opaque parameter passed to
* {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
* @param arg2 An opaque parameter passed to
* {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
* @param startIndex The index into the mCallbacks to start notifying.
* @param endIndex One past the last index into mCallbacks to notify.
* @param bits A bit field indicating which callbacks have been removed and shouldn't
* be notified.
*/
private void notifyCallbacks(T sender, int arg, A arg2, final int startIndex,
final int endIndex, final long bits) {
long bitMask = 1;
for (int i = startIndex; i < endIndex; i++) {
if ((bits & bitMask) == 0) {
mNotifier.onNotifyCallback(mCallbacks.get(i), sender, arg, arg2);
}
bitMask <<= 1;
}
}
而mNotifier其实就是在BaseObservable构造PropertyChangeRegistry传入的参数。
public CallbackRegistry(NotifierCallback notifier) {
mNotifier = notifier;
}
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实际上就是调用mCallbacks.get(i).onPropertyChanged(),我们进一步看看
private static class WeakPropertyListener extends Observable.OnPropertyChangedCallback
implements ObservableReference<Observable> {
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);
}
}
可以看到onPropertyChanged调用了编译生成的ActivityMainBinding的onFieldChange方法将mDirtyFlags标识为需要重新绑定VM到V上,并且调用requestRebind方法。requestRebind方法前面已经介绍过,最后能调用到ActivityMainBinding的executeBindings进行界面绑定的工作。
DataBinding在旧版本中是不支持这个功能的,后来才完善了这个功能。例如
在使用双向绑定后,看看生成的ActivityMainBinding有什么变化?可以发现在executeBindings里多了一点代码:
@Override
protected void executeBindings() {
// ……
if ((dirtyFlags & 0x4L) != 0) {
// api target 1
android.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.button, (android.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged)null, (android.databinding.adapters.TextViewBindingAdapter.OnTextChanged)null, (android.databinding.adapters.TextViewBindingAdapter.AfterTextChanged)null, buttonandroidTextAttrChanged);
}
}
在这个方法里调用了setTextWatcher去监听Button的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 onTextChanged(CharSequence s, int start, int before, int count) {
// …...
if (textAttrChanged != null) {
textAttrChanged.onChange();
}
}
// …...
};
}
// …...
if (newValue != null) {
view.addTextChangedListener(newValue);
}
}
当V发生变化时,会调buttonandroidTextAttrChanged的onChange方法。
// Inverse Binding Event Handlers
private android.databinding.InverseBindingListener buttonandroidTextAttrChanged = new android.databinding.InverseBindingListener() {
@Override
public void onChange() {
// Inverse of user.name.get()
// is user.name.set((java.lang.String) callbackArg_0)
java.lang.String callbackArg_0 = android.databinding.adapters.TextViewBindingAdapter.getTextString(button);
// localize variables for thread safety
// user.name
android.databinding.ObservableField userName = null;
// user.name != null
boolean userNameJavaLangObjectNull = false;
// user != null
boolean userJavaLangObjectNull = false;
// user
com.example.databindingdemo.User user = mUser;
// user.name.get()
java.lang.String userNameGet = null;
userJavaLangObjectNull = (user) != (null);
if (userJavaLangObjectNull) {
userName = user.getName();
userNameJavaLangObjectNull = (userName) != (null);
if (userNameJavaLangObjectNull) {
userName.set(((java.lang.String) (callbackArg_0)));
}
}
}
};
在上面buttonandroidTextAttrChanged的onChange回调里,将变动后的值赋值到VM上。这样,V的变化就自动同步到VM上了。
在介绍双向绑定的原理时,提过“在layout中设置了id属性的,可以直接通过ViewBinding.xxx进行访问”,这种方式避免了findViewById的操作,那本质上是怎么实现的呢?
在初始化时,我们是通过DataBindingUtil.setContentView来建立layout与ViewBinding的联系,因此从这个方法开始分析:
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);
}
setContentView直接调用同名方法,而在这个同名方法内,先设置为activity的contentView,然后将contentView传入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与ViewBinding之间的映射。
public android.databinding.ViewDataBinding getDataBinder(android.databinding.DataBindingComponent bindingComponent, android.view.View view, int layoutId) {
switch(layoutId) {
case com.example.databindingdemo.R.layout.activity_main:
return com.example.databindingdemo.databinding.ActivityMainBinding.bind(view, bindingComponent);
}
return null;
}
这里根据layoutId调用对应ViewBinding的bind方法,ViewBinding的名称是根据layoutId生成的,例如activity_main对应的ViewBinding名称为ActivityMainBinding。
public static ActivityMainBinding bind(android.view.View view, 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);
}
bind方法里面构造了一个ActivityMainBinding对象。
public ActivityMainBinding(android.databinding.DataBindingComponent bindingComponent, View root) {
super(bindingComponent, root, 2);
final Object[] bindings = mapBindings(bindingComponent, root, 2, sIncludes, sViewsWithIds);
this.button = (android.widget.Button) bindings[1];
this.button.setTag(null);
this.mboundView0 = (android.widget.RelativeLayout) bindings[0];
this.mboundView0.setTag(null);
setRootTag(root);
// listeners
invalidateAll();
}
在构造函数里面,我们看到对button、mboundView0进行了强转赋值操作,因此可以知道mapBindings得到的bindings数组就是View数组。mapBindings方法比较长,我们分成三部分来看看:
① 从view的tag中获取缓存,防止多次初始化
private static void mapBindings(DataBindingComponent bindingComponent, View view,
Object[] bindings, IncludedLayouts includes, SparseIntArray viewsWithIds,
boolean isRoot) {
// ……
final ViewDataBinding existingBinding = getBinding(view);
if (existingBinding != null) {
return;
}
final String tag = (String) view.getTag();
// ……
}
static ViewDataBinding getBinding(View v) {
if (v != null) {
if (USE_TAG_ID) {
return (ViewDataBinding) v.getTag(R.id.dataBinding);
} else {
final Object tag = v.getTag();
if (tag instanceof ViewDataBinding) {
return (ViewDataBinding) tag;
}
}
}
return null;
}
② 将view存储在bindings数组内,分为三种情况,与前面生成的ViewBinding内的view变量类型一致,一为根布局,tag以layout开头;二为设置了@{}的,tag以binding开头;三为设置了id属性的。
private static void mapBindings(DataBindingComponent bindingComponent, View view,
Object[] bindings, IncludedLayouts includes, SparseIntArray viewsWithIds,
boolean isRoot) {
// ……
if (isRoot && tag != null && tag.startsWith("layout")) { // tag以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)) { // tag以binding_开头
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) { // 设置了id的
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;
}
}
}
// ……
}
③ 这部分判断如果是ViewGroup的话,则判断子View是不是include的,如果是的话,则使用DataBindingUtil.bind进行递归;如果不是include,则直接使用mapBindings进行递归。
private static void mapBindings(DataBindingComponent bindingComponent, View view,
Object[] bindings, IncludedLayouts includes, SparseIntArray viewsWithIds,
boolean isRoot) {
// ……
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) { // include的layout,使用DataBindingUtil.bind进行递归
String childTag = (String) child.getTag();
if (childTag != null && 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) { // 不是include的layout ,使用mapBindings进行递归
mapBindings(bindingComponent, child, bindings, includes, viewsWithIds, false);
}
}
}
// ……
}
通过这三个步骤,递归得到最后的bindings数组。如果设置了id的,就将view变量设置为public,这样就避免了findViewById的代码。这种方式从性能上比findViewById高效,因为databinding只需要遍历一次view数,而findViewById多次调用会遍历多次。
databinding在编译时会生成代码,利用了apt(annotation-processing-tool),apt在ButterKnife与EventBus3都有运用到,有兴趣的可以看看之前分析的文章:
- butterknife源码分析:代码分析
- EventBus源码分析
databinding仓库在https://android.googlesource.com/platform/frameworks/data-binding/+/master
这里对主要的脉络分析下,不细入研究。
先从注解处理器开始,对应的类为 compiler / src / main / java / android / databinding / annotationprocessor / ProcessDataBinding.java
@Override
public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (mProcessingSteps == null) {
initProcessingSteps();
}
final BindingBuildInfo buildInfo = BuildInfoUtil.load(roundEnv);
if (buildInfo == null) {
return false;
}
boolean done = true;
for (ProcessingStep step : mProcessingSteps) {
try {
done = step.runStep(roundEnv, processingEnv, buildInfo) && done;
} catch (JAXBException e) {
L.e(e, "Exception while handling step %s", step);
}
}
// ……
return done;
}
process内循环执行了ProcessingStep数组,ProcessingStep数组内有ProcessMethodAdapters、ProcessExpressions、ProcessBindable三个。
mProcessingSteps = Arrays.asList(
new ProcessMethodAdapters(),
new ProcessExpressions(),
new ProcessBindable()
);
① ProcessMethodAdapters负责处理@BindingAdapter 、@BindingMethods、@BindingConversion等注解,并将相关的信息通过SetterStore保存起来。
public class ProcessMethodAdapters extends ProcessDataBinding.ProcessingStep {
// ……
@Override
public boolean onHandleStep(RoundEnvironment roundEnv,
ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo) {
// ……
addBindingAdapters(roundEnv, processingEnvironment, store);
addRenamed(roundEnv, store);
addConversions(roundEnv, store);
addUntaggable(roundEnv, store);
addInverseAdapters(roundEnv, processingEnvironment, store);
addInverseMethods(roundEnv, store);
try {
store.write(buildInfo.modulePackage(), processingEnvironment);
} catch (IOException e) {
L.e(e, "Could not write BindingAdapter intermediate file.");
}
return true;
}
// ……
}
② ProcessExpressions负责处理layout文件,将layout文件转换为前面所提到的activity_main.xml(正常布局)和activity_main-layout.xml(包含绑定信息),总结起来就是解析xml布局,解析里面的layout、import、variables等标签,然后生成文件。这里就不细入了,代码量比较大。
③ ProcessBindable负责生成BR类,例如BR.user等。
private void initProcessingSteps() {
// ……
Callback dataBinderWriterCallback = new Callback() {
CompilerChef mChef;
BRWriter mBRWriter;
boolean mLibraryProject;
int mMinSdk;
// ……
private void considerWritingMapper() {
if (mLibraryProject || mChef == null || mBRWriter == null) {
return;
}
mChef.writeDataBinderMapper(mMinSdk, mBRWriter);
}
@Override
public void onBrWriterReady(BRWriter brWriter) {
Preconditions.checkNull(mBRWriter, "Cannot set br writer twice");
mBRWriter = brWriter;
considerWritingMapper();
}
};
//……
}
生成好BR类之后,会回调considerWritingMapper方法。最后利用compiler / src / main / kotlin / android / databinding / tool / writer / DataBinderWriter.kt与compiler / src / main / kotlin / android / databinding / tool / writer / LayoutBinderWriter.kt生成DataBinderMapper与ViewBinding类。这里也不细入了,是用kotlin写的。
到这里,关于DataBinding的源码解析就结束了。那么假如我是面试官,问你一下三个问题,你们如何作答?
- Databinding如何避免findViewById的?
- DataBinding中是如何实现当V的变化同步更新到VM的?
- DataBinding中是如何实现将VM的数据绑定到V的?