前言
现在公司用的框架是MVVM,用到MVVM肯定少不了databinding。
最近业务上遇到一个问题,需要动态生成一张图片,并将图片分享出去。我的思路就是写一个xml动态生成一个View,利用databinding绑定这个View,然后获取数据刷新对应的ViewModel,再将View转化成Bitmap分享出去。结果发现,数据并没有填充到View上。这是为什么呢?
当时的猜想,可能是因为我需要的这个View并没有加载到屏幕上而databinding应该是和生命周期有关的,所以databinding不做处理。
这周末正好有空,所以来了解一下databinding。
databinding生成的文件
我们使用databinding,会绑定一个xml,这个过程中,编译器会默默地帮我们生成一系列文件,如下图:
当然以上只是部分,还有xml文件等等。
activity_test.xml
位于app/build/intermediates/data-binding-layout-out/debug/layout目录下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent" android:tag="layout/activity_test_0" xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:tag="binding_1" />
</RelativeLayout>
对比最初的activity_test.xml:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewModel"
type="com.example.tsnt.mvvm.TestVM" />
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:text="@{viewModel.bottomContent}" />
</RelativeLayout>
</layout>
可以看出,生成的xml与原始的xml差不多,区别是:
1.去掉了layout标签
2.增加了tag标记,这里是android:tag="layout/activity_test_0"
我们使用DataBinding添加layout标签后的布局文件会经过DataBinding处理成正常的布局文件activity_test.xml与包含绑定信息的文件activity_test-layout.xml。
activity_test-layout.xml
位置于app/build/intermediates/data-binding-info/debug目录下:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Layout absoluteFilePath="/Users/zhangxiaozong/Documents/projects/others/AndroidStudy/app/src/main/res/layout/activity_test.xml" directory="layout"
isMerge="false"
layout="activity_test" modulePackage="com.example.tsnt">
<Variables name="viewModel" declared="true" type="com.example.tsnt.mvvm.TestVM">
<location endLine="7" endOffset="49" startLine="5" startOffset="8" />
</Variables>
<Targets>
<Target tag="layout/activity_test_0" view="RelativeLayout">
<Expressions />
<location endLine="26" endOffset="20" startLine="10" startOffset="4" />
</Target>
<Target tag="binding_1" view="TextView">
<Expressions>
<Expression attribute="android:text" text="viewModel.bottomContent">
<Location endLine="25" endOffset="52" startLine="25" startOffset="12" />
<TwoWay>false</TwoWay>
<ValueLocation endLine="25" endOffset="50" startLine="25" startOffset="28" />
</Expression>
</Expressions>
<location endLine="25" endOffset="55" startLine="20" startOffset="8" />
</Target>
<Target id="@+id/content" view="TextView">
<Expressions />
<location endLine="18" endOffset="50" startLine="14" startOffset="8" />
</Target>
</Targets>
</Layout>
它会告诉我们生成的activity_test.xml对应的tag的数据信息。
ActivityTestBinding
这就是我们生成之后可以直接调用的ViewDataBinding:
public class ActivityTestBinding extends android.databinding.ViewDataBinding {
private static final android.databinding.ViewDataBinding.IncludedLayouts sIncludes;
private static final android.util.SparseIntArray sViewsWithIds;
static {
sIncludes = null;
sViewsWithIds = new android.util.SparseIntArray();
sViewsWithIds.put(R.id.content, 2);
}
// views
public final android.widget.TextView content;
private final android.widget.RelativeLayout mboundView0;
private final android.widget.TextView mboundView1;
// variables
private com.example.tsnt.mvvm.TestVM mViewModel;
……
}
其中可以看到ActivityTestBinding里面持有了一些view的变量,那是不是布局中所有的view都会在ActivityTestBinding中?其实不是,ActivityTestBinding里的view变量有三种类型:
① 根布局
② 含有@{}绑定的view
③ 含有id属性的view
BR
位于app/build/generated/source/apt/debug/com/android/databinding/library/baseAdapters目录下:
public class BR {
public static final int _all = 0;
public static final int viewModel = 1;
}
DataBinderMapper
位于app/build/generated/source/apt/debug/android/databinding/目录下:
class DataBinderMapper {
final static int TARGET_MIN_SDK = 16;
public DataBinderMapper() {
}
public android.databinding.ViewDataBinding getDataBinder(android.databinding.DataBindingComponent bindingComponent, android.view.View view, int layoutId) {
switch(layoutId) {
case com.example.tsnt.R.layout.activity_test:
return com.example.tsnt.databinding.ActivityTestBinding.bind(view, bindingComponent);
}
return null;
}
android.databinding.ViewDataBinding getDataBinder(android.databinding.DataBindingComponent bindingComponent, android.view.View[] views, int layoutId) {
switch(layoutId) {
}
return null;
}
int getLayoutId(String tag) {
if (tag == null) {
return 0;
}
final int code = tag.hashCode();
switch(code) {
case 628154894: {
if(tag.equals("layout/activity_test_0")) {
return com.example.tsnt.R.layout.activity_test;
}
break;
}
}
return 0;
}
String convertBrIdToString(int id) {
if (id < 0 || id >= InnerBrLookup.sKeys.length) {
return null;
}
return InnerBrLookup.sKeys[id];
}
private static class InnerBrLookup {
static String[] sKeys = new String[]{
"_all"
,"viewModel"};
}
}
DataBinderMapper主要提供了从布局文件layoutId到ViewBinding类的映射。
绑定ViewModel的过程
首先来看TestActivity主要代码:
public class TestActivity extends AppCompatActivity {
private ActivityTestBinding dataBinding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initView();
}
private void initView() {
dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_test);
dataBinding.content.setText("Hello, databinding!");
dataBinding.setViewModel(new TestVM());
}
}
其中关键的代码是:dataBinding.setViewModel(new TestVM());
。
这里调用的是ActivityTestBinding的setViewModel():
public void setViewModel(com.example.tsnt.mvvm.TestVM viewModel) {
this.mViewModel = viewModel;
synchronized(this) {
mDirtyFlags |= 0x2L;
}
notifyPropertyChanged(BR.viewModel);
super.requestRebind();
}
这里通过notifyPropertyChanged(BR.viewModel)
去发出ViewModel数据变化的通知。
然后进入ViewDataBinding的requestRebind():
protected void requestRebind() {
synchronized (this) {
if (mPendingRebind) {
return;
}
mPendingRebind = true;
}
if (USE_CHOREOGRAPHER) {
mChoreographer.postFrameCallback(mFrameCallback);
} else {
mUIThreadHandler.post(mRebindRunnable);
}
}
其中mPendingRebind的定义如下:
/**
* Flag indicates that there are pending bindings that need to be reevaluated.
*/
private boolean mPendingRebind = false;
在requestRebind()中获取同步锁,如果已经处于pendingRebind状态就直接返回,如果不是pendingRebind状态就继续,并且标记mPendingRebind为true。
再来看USE_CHOREOGRAPHER的定义:
private static final boolean USE_CHOREOGRAPHER = SDK_INT >= 16;
这里根据系统版本做了点不同的处理,16及以上的,会往mChoreographer发一个mFrameCallback;否则直接往UI线程发一个mRebindRunnable。其实这里俩个分支的结果基本一致,mChoreographer会在界面刷新时执行mRebindRunnable,Choreographer是系统版本16后引入的用于解决UI卡顿的,当收到VSYNC(定时中断)时,在doFrame()里去执行相应的操作。
接着我们来看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;
}
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,然后对系统版本进行了判断。如果是19及以上时,会判断View是否加载到了屏幕上,如果没有的话,则对这个attach的状态进行监听。这就是我遇到问题的原因,因为View一直没有加载到屏幕上,所以监听也一直没回调。
正常情况下,最终都会执行executePendingBindings():
/**
* Evaluates the pending bindings, updating any Views that have expressions bound to
* modified variables. This <b>must</b> be run on the UI thread.
*/
public void executePendingBindings() {
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,表明当前没有需要需要绑定的数据,则返回不处理。
接下来又进行了一系列判断,如果没被终止,会进入executeBindings(),执行最终绑定。executeBindings()是一个抽象方法,由ViewDataBinding的子类去实现。
看到ActivityTestBinding的executeBindings():
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
com.example.tsnt.mvvm.TestVM viewModel = mViewModel;
android.databinding.ObservableField<java.lang.String> bottomContentViewMod = null;
java.lang.String bottomContentViewMod1 = null;
if ((dirtyFlags & 0x7L) != 0) {
if (viewModel != null) {
// read viewModel.bottomContent
bottomContentViewMod = viewModel.bottomContent;
}
updateRegistration(0, bottomContentViewMod);
if (bottomContentViewMod != null) {
// read viewModel.bottomContent.get()
bottomContentViewMod1 = bottomContentViewMod.get();
}
}
// batch finished
if ((dirtyFlags & 0x7L) != 0) {
// api target 1
android.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView1, bottomContentViewMod1);
}
}
这里做的操作就是绑定ViewModel到对应的View,ActivityTestBinding就持有了ViewModel的引用。
然后对ViewModel中绑定的参数注册了监听,最后对View中的数据进行初始化。
接下来看如何注册监听的,进入updateRegistration():
return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
来看CREATE_PROPERTY_LISTENER的定义:
/**
* 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();
}
};
然后继续看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);
}
}
CREATE_PROPERTY_LISTENER是一个CreateWeakListener对象,它是一个创建WeakPropertyListener的工厂类。
WeakPropertyListener内有变量WeakListener,能通过它的getListener()得到WeakListener。
那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);
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;
}
}
原来它是一个弱引用,它持有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()。
然后调用mObservable的addListener(),mObservable就是在构造WeakListener传入的第三个参数。以上的代码,WeakPropertyListener在构造函数里构造WeakListener是传入的是this,因此mObservable其实就是WeakPropertyListener。
在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的引用。
VM变化如何通知View
我们知道,如果要达到VM变化时自动绑定到View上,有下面俩种方式:
继承自BaseObservable,在getter上增加@Bindable注解,在setter里增加代码notifyPropertyChanged(BR.xxx)。
无需继承,需要将属性替换为Observable类,例如ObservableInt、ObservableField等。
这两种方式本质上都是一样的。在第二种方式中,当属性发生变化时,会调用set()进而调用notifyChange(),而notifyChange()与notifyPropertyChanged()做的事情都是一样的,都是调用mCallbacks.notifyCallbacks()去发出通知:
public void set(T value) {
if (value != mValue) {
mValue = value;
notifyChange();
}
}
/**
* Notifies listeners that all properties of this instance have changed.
*/
public synchronized void notifyChange() {
if (mCallbacks != null) {
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
* <code>BR</code> to be used as <code>fieldId</code>.
*
* @param fieldId The generated BR id for the Bindable field.
*/
public void notifyPropertyChanged(int fieldId) {
if (mCallbacks != null) {
mCallbacks.notifyCallbacks(this, fieldId, null);
}
}
这里的mCallbacks就是刚才添加的WeakPropertyListener。
我们一层层往下跟踪,notifyCallbacks(CallbackRegistry) -> notifyRecurse(CallbackRegistry) -> notifyRemainder(CallbackRegistry) -> notifyFirst64(CallbackRegistry),最后到达下面这个方法:
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的定义:
/** The notification mechanism for notifying an event. */
private final NotifierCallback<C, T, A> mNotifier;
而mNotifier其实就是在BaseObservable构造PropertyChangeRegistry传入的参数:
@Override
public synchronized void addOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
if (mCallbacks == null) {
mCallbacks = new PropertyChangeRegistry();
}
mCallbacks.add(callback);
}
public class PropertyChangeRegistry extends
CallbackRegistry<Observable.OnPropertyChangedCallback, Observable, Void> {
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);
}
};
public PropertyChangeRegistry() {
super(NOTIFIER_CALLBACK);
}
/**
* Notifies registered callbacks that a specific property has changed.
*
* @param observable The Observable that has changed.
* @param propertyId The BR id of the property that has changed or BR._all if the entire
* Observable has changed.
*/
public void notifyChange(Observable observable, int propertyId) {
notifyCallbacks(observable, propertyId, null);
}
}
因此调用mNotifier.onNotifyCallback实际上就是调用mCallbacks.get(i).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();
}
}
可以看到onPropertyChanged调用了编译生成的ActivityTestBinding的onFieldChange方法将mDirtyFlags标识为需要重新绑定VM到V上,并且调用requestRebind方法。requestRebind方法前面已经介绍过,最后能调用到ActivityTestBinding的executeBindings()进行界面绑定的工作。
V的变化如何同步到VM
DataBinding在旧版本中是不支持这个功能的,后来才完善了这个功能。
现在对最初的xml进行修改:把android:text="@{viewModel.bottomContent}"
改成android:text="@{viewModel.bottomContent}"
。
在使用双向绑定后,看看生成的ActivityMainBinding有什么变化?可以发现在executeBindings里多了一点代码:
if ((dirtyFlags & 0x4L) != 0) {
// api target 1
android.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.mboundView1, (android.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged)null, (android.databinding.adapters.TextViewBindingAdapter.OnTextChanged)null, (android.databinding.adapters.TextViewBindingAdapter.AfterTextChanged)null, mboundView1androidTe);
}
在这个方法里调用了setTextWatcher去监听TextView的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);
}
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);
}
}
当V发生变化时,会调用textAttrChanged的onChange()即ActivityDataBinding中setTextWatcher()时传入的成员变量的onChange():
private android.databinding.InverseBindingListener mboundView1androidTe = new android.databinding.InverseBindingListener() {
@Override
public void onChange() {
// Inverse of viewModel.bottomContent.get()
// is viewModel.bottomContent.set((java.lang.String) callbackArg_0)
java.lang.String callbackArg_0 = android.databinding.adapters.TextViewBindingAdapter.getTextString(mboundView1);
// localize variables for thread safety
// viewModel.bottomContent
android.databinding.ObservableField<java.lang.String> bottomContentViewMod = null;
// viewModel != null
boolean viewModelObjectnull = false;
// viewModel
com.example.tsnt.mvvm.TestVM viewModel = mViewModel;
// viewModel.bottomContent != null
boolean BottomContentViewMod1 = false;
// viewModel.bottomContent.get()
java.lang.String BottomContentViewMod2 = null;
viewModelObjectnull = (viewModel) != (null);
if (viewModelObjectnull) {
bottomContentViewMod = viewModel.bottomContent;
BottomContentViewMod1 = (bottomContentViewMod) != (null);
if (BottomContentViewMod1) {
bottomContentViewMod.set((java.lang.String) (callbackArg_0));
}
}
}
};
在上面textAttrChanged的onChange()回调里,将变动后的值赋值到VM上。这样,V的变化就自动同步到VM上了。
如何避免findViewById
在初始化时,我们是通过DataBindingUtil.setContentView()来建立layout与ViewBinding的联系,因此从这个方法开始分析:
public static <T extends ViewDataBinding> T setContentView(Activity activity, int layoutId) {
return setContentView(activity, layoutId, sDefaultComponent);
}
public static <T extends ViewDataBinding> 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 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);
}
}
static <T extends ViewDataBinding> 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.tsnt.R.layout.activity_test:
return com.example.tsnt.databinding.ActivityTestBinding.bind(view, bindingComponent);
}
return null;
}
public static ActivityTestBinding bind(android.view.View view, android.databinding.DataBindingComponent bindingComponent) {
if (!"layout/activity_test_0".equals(view.getTag())) {
throw new RuntimeException("view tag isn't correct on view:" + view.getTag());
}
return new ActivityTestBinding(bindingComponent, view);
}
这里根据layoutId调用对应ViewBinding的bind方法,ViewBinding的名称是根据layoutId生成的,例如activity_test对应的ViewBinding名称为ActivityTestBinding,然后在ActivityTestBinding的静态方法bind()中构造了一个ActivityTestBinding对象:
public ActivityTestBinding(android.databinding.DataBindingComponent bindingComponent, View root) {
super(bindingComponent, root, 1);
final Object[] bindings = mapBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds);
this.content = (android.widget.TextView) bindings[2];
this.mboundView0 = (android.widget.RelativeLayout) bindings[0];
this.mboundView0.setTag(null);
this.mboundView1 = (android.widget.TextView) bindings[1];
this.mboundView1.setTag(null);
setRootTag(root);
// listeners
invalidateAll();
}
在构造函数里面,我们看到对button、mboundView0、mboundView1进行了强转,因此可以知道mapBindings得到的bindings数组就是View数组。mapBindings方法比较长,我们分成三部分来看看:
① 从view的tag中获取缓存,防止多次初始化
final int indexInIncludes;
final ViewDataBinding existingBinding = getBinding(view);
if (existingBinding != null) {
return;
}
② 将view存储在bindings数组内,分为三种情况,与前面生成的ViewBinding内的view变量类型一致,一为根布局,tag以layout开头;二为设置了@{}的,tag以binding开头;三为设置了id属性的。
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;
}
}
}
③ 这部分判断如果是ViewGroup的话,则判断子View是不是include的,如果是的话,则使用DataBindingUtil.bind进行递归;如果不是include,则直接使用mapBindings进行递归。
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多次调用会遍历多次。
本文大部分内容参考:DataBinding源码解析