此篇博客参考 海的鼻涕
Jetpack ---- DataBinding完全解析(四)DataBinding源码分析补充
setUser
这里面的代码比较简单,主要分析一下updateRegistration方法。第一个入参localFieldId与第二个入参的userName一一对应。
updateRegistration
ViewDataBinding
protected boolean updateRegistration(int localFieldId, Observable observable) {
return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
}
updateRegistration第三个参数传了CREATE_PROPERTY_LISTENER,我们先看看CREATE_PROPERTY_LISTENER是什么,再分析updateRegistration方法。
ViewDataBinding
/**
* 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<Observable> mListener;
public WeakPropertyListener(ViewDataBinding binder, int localFieldId) {
mListener = new WeakListener<Observable>(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<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;
}
// ……
}
从上面知道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;
}
registerTo
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<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);
}
}
// ......
}
然后调用mObservable的addListener方法,mObservable就是在构造WeakListener传入的第三个参数。根据下面的代码,WeakPropertyListener在构造函数里构造WeakListener是传入的是this,因此mObservable其实就是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 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);
}
当调用setUser后WeakPropertyListener中最后会回调onPropertyChanged方法
@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) {
if (mInLiveDataRegisterObserver) {
// We're in LiveData registration, which always results in a field change
// that we can ignore. The value will be read immediately after anyway, so
// there is no need to be dirty.
return;
}
boolean result = onFieldChange(mLocalFieldId, object, fieldId);
if (result) {
requestRebind();
}
}
然后会判断当观察到的对象发生变化时UI是否需要更改,如果需要调用requestRebind(),这个方法上面已经分析过了。
ViewDataBinding
/**
* 当观察到的对象发生变化时调用。 设置适当的脏标志(如果适用)。
* @param localFieldId此对象所在的mLocalFieldObservers的索引。
* @param对象已更改的对象。
* @param fieldId要更改的字段的BR ID;如果未通知特定字段,则返回_all。
* @return如果此更改应导致UI更改,则为true。
*/
protected abstract boolean onFieldChange(int localFieldId, Object object, int fieldId);
ActivityMainBindingImpl
@Override
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
switch (localFieldId) {
case 0 :
return onChangeUser((com.wll.android.sourcecodeanalysis.User) object, fieldId);
}
return false;
}
private boolean onChangeUser(com.wll.android.sourcecodeanalysis.User User, int fieldId) {
if (fieldId == BR._all) {
synchronized(this) {
mDirtyFlags |= 0x1L;
}
return true;
}
else if (fieldId == BR.name) {
synchronized(this) {
mDirtyFlags |= 0x2L;
}
return true;
}
return false;
}
最后调用到的 ActivityMainBindingImpl.executeBindings 会根据脏标志进行判断执行什么ui操作。
ActivityMainBindingImpl
这样一来V和VM的联系就通过ViewDatabinding建立起来了。V(Activity)内有ViewDatabinding,而ViewDatabinding里持有各个V(布局中的元素)的引用。ViewDataBinding有VM的变量,而VM内的PropertyChangeRegistry监听实则为WeakPropertyListener,WeakPropertyListener得到的WeakListener能获取到ViewDatabinding的引用。
VM变化如何通知View
前面我们已经讲过了V和VM是如何建立起联系的,接下来我们跟着流程看看具体如何?
我们知道,如果要达到VM变化时自动绑定到View上,有下面俩种方式:
- 继承自BaseObservable,在getter上增加@Bindable注解,在setter里增加代码notifyPropertyChanged(BR.xxx)。
- 无需继承,需要将属性替换为Observable类,例如ObservableInt、ObservableField等。
这两种本质上都是一样的。在第二种方式中,当属性发生变化时,会调用notifyChange,而notifyChange与notifyPropertyChanged做的事情都是一样的,都是调用mCallbacks.notifyCallbacks去通知。
mCallbacks添加监听的过程上面我们已经分析过了,也可以通过函数调用,find usages,一层层往上寻找。BaseObservable.addOnPropertyChangedCallback(BaseObservable) -> ViewDataBinding.WeakPropertyListener.addListener(WeakPropertyListener) -> ViewDataBinding.WeakListener.setTarget(WeakListener)-> ViewDataBinding.registerTo
。到这里,就和上面的registerTo方法“会师”了,因此在调用binding.setUser(user)绑定时,就在Observable上加了监听,观察者就是WeakPropertyListener。当VM发生变化时,会通过mCallbacks.notifyCallbacks将变化发送出去。
那当VM发生变化时,notifyCallbacks又是如何将变化发送出去呢?
我们一层层往下跟踪,CallbackRegistry.notifyCallbacks() -> CallbackRegistry.notifyRecurse() -> CallbackRegistry.notifyRemainder(CallbackRegistry) -> CallbackRegistry.notifyFirst64(CallbackRegistry)
,最后到达下面这个方法:
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<C, T, A> notifier) {
mNotifier = notifier;
}
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实际上就是调用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);
}
}
可以看到onPropertyChanged调用了编译生成的ActivityMainBinding的onFieldChange方法将mDirtyFlags标识为需要重新绑定VM到V上,并且调用requestRebind方法。requestRebind方法前面已经介绍过,最后能调用到ActivityMainBinding的executeBindings进行界面绑定的工作。这部分上面已经分析过了。
V的变化如何同步到VM
DataBinding在旧版本中是不支持这个功能的,后来才完善了这个功能。例如
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@={user.name}" />
在使用双向绑定后,看看生成的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。
TextViewBindingAdapter
@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<java.lang.String> 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上了。
如何避免findViewById
在介绍双向绑定的原理时,提过“在layout中设置了id属性的,可以直接通过ViewBinding.xxx进行访问”,这种方式避免了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.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();
}
原作者生成的ActivityMainBindingImpl代码应该是之前版本的代码,3.6.4版本生成的代码如下:
在构造函数里面,我们看到对button、mboundView0进行了强转赋值操作,因此可以知道mapBindings得到的bindings数组就是View数组。mapBindings方法比较长,我们分成三部分来看看:
① 从view的tag中获取缓存,防止多次初始化
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 ViewDataBinding existingBinding = getBinding(view);
if (existingBinding != null) {
return;
}
final String tag = (String) view.getTag();
// ……
}
static ViewDataBinding getBinding(View v) {
if (v != null) {
return (ViewDataBinding) v.getTag(R.id.dataBinding);
}
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多次调用会遍历多次。
ActivityMainBinding.inflate(getLayoutInflater())是如何建立Activity和View的关联的
ActivityMainBinding
@NonNull
public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater) {
return inflate(inflater, DataBindingUtil.getDefaultComponent());
}
/**
* This method receives DataBindingComponent instance as type Object instead of
* type DataBindingComponent to avoid causing too many compilation errors if
* compilation fails for another reason.
* https://issuetracker.google.com/issues/116541301
* @Deprecated Use DataBindingUtil.inflate(inflater, R.layout.activity_main, null, false, component)
*/
@NonNull
@Deprecated
public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater,
@Nullable Object component) {
return ViewDataBinding.<ActivityMainBinding>inflateInternal(inflater, R.layout.activity_main, null, false, component);
}
这整个流程跟DataBindingUtil.setContentView类似最后都会调用DataBindingUtil.bind。