- 本篇笔记主要按照三个方面来讲,单项绑定和双向绑定以及源码分析(使用方面会是略写的方式,源码分析是重点);
一.简介
- 数据绑定
- 好处
- 开发中不需要持有控件的引用
- 拥有双向绑定的特性
- 数据与UI同步(设计的初衷,Model层数据发生变化,能激活UI的更新)
- 项目中可以分担部分UI的简单逻辑
二.单向绑定和双向绑定
2.1.单向绑定
- 有三种
2.1.1.JavaBean继承BaseObservable
- 在set方法中添加notifyChanged,在set方法中添加notifyChange(会更新所有的UI,可以通过notifyPropertyChanged(BR.xxx)方式来实现只更新单个属性)的方法
//单向绑定的第一种方法
//弊端:业务改变的时候,javabean需要修改
//JavaBean
public class PersonInfo extends BaseObservable {
private String userName;
private String passwd;
public PersonInfo(String userName, String passwd) {
this.userName = userName;
this.passwd = passwd;
}
@Bindable
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
notifyPropertyChanged(BR.userName);
}
@Bindable
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
notifyPropertyChanged(BR.passwd);
}
}
- 持有ObservableField;
- 在layout中使用viewmodle(推荐);
2.2.双向绑定
- 知道了单向绑定,双向绑定很简单,增加=即可;
2.3.其它
- 通过BindingAdapter来自定义Setter
- 设置数据到列表控件中
- 以上两个业务场景可以参考-DataBinding从基本使用到核心源码解析,看不懂的兄弟跟我一起去摆摊
三.源码分析一
- 重点来袭
- 先补充个技巧:我们在分析源码的时候,源码入口的寻找方法,主要根据以下几点进行寻找
- 三方SDK本身的逻辑,如Okhttp、Retrofit、Glide、Rxjava
- 一定要注意,是否Hook了Application、Framework的源码,如:组件化、插件化、热修复;
- activity或者fragment的生命周期相关的代码,如:Lifecycle、Livedata
- Apt生成的代码,如:Databinding、Butterknife、Dagger2、Hilt
- 代码的灵异事件(比如:不符合语法规则或不符合自己想要的逻辑等),需要考虑是否有gradle任务做了字节码插桩;
- 三方SDK本身的逻辑,如Okhttp、Retrofit、Glide、Rxjava
- 另外,在看源码的过程中,要掌握正确的分析过程,画时序图(代码的执行)和类图(整体的结构);
- 源码分析一部分,我们要做的是,探讨为什么使用databinding之后,不需要做findViewbyId;
3.1.布局文件
- 我们按照databinding的要求写布局和设置布局后,databinding会自动生成对应的java文件和xml文件。我们先分析布局文件。比如:写了一个登录的页面,名称为activity_login。apt会自动生成两个布局文件。
3.2.源码的入口
-
//1.入口 DataBindingUtil.setContentView(this, initContentView()) //2.setContentView public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity,int layoutId) { return setContentView(activity, layoutId, sDefaultComponent); } //3.setContentView public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity, int layoutId, @Nullable DataBindingComponent bindingComponent) { activity.setContentView(layoutId); View decorView = activity.getWindow().getDecorView(); ViewGroup contentView = (ViewGroup) decorView.findViewById(android.R.id.content); return bindToAddedViews(bindingComponent, contentView, 0, layoutId); } //4.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); } } //6.1.sMapper是一个成员变量,指向DataBinderMapperImpl,DataBinderMapperImpl是apt自动生成的 private static DataBinderMapper sMapper = new DataBinderMapperImpl(); //5.bind static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View root, int layoutId) { //6.2.getDataBinder,在3.3中分析 return (T) sMapper.getDataBinder(bindingComponent, root, layoutId); }
3.3.DataBinderMapper
public abstract class DataBinderMapper {
//6.3.查看实现类DataBinderMapperImpl
public abstract ViewDataBinding getDataBinder(DataBindingComponent bindingComponent, View view,
int layoutId);
public abstract ViewDataBinding getDataBinder(DataBindingComponent bindingComponent,
View[] view, int layoutId);
public abstract int getLayoutId(String tag);
public abstract String convertBrIdToString(int id);
@NonNull
public List<DataBinderMapper> collectDependencies() {
// default implementation for backwards compatibility.
return Collections.emptyList();
}
}
//6.4.实现类DataBinderMapperImpl的getDataBinder方法
@Override
public ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) {
int localizedLayoutId = INTERNAL_LAYOUT_ID_LOOKUP.get(layoutId);
if(localizedLayoutId > 0) {
final Object tag = view.getTag();
if(tag == null) {
throw new RuntimeException("view must have a tag");
}
switch(localizedLayoutId) {
case LAYOUT_ACTIVITYLOGIN: {
//6.5.如果tag为layout/activity_login_0,就new出ActivityLoginBindingImpl
if ("layout/activity_login_0".equals(tag)) {
//7.分析ActivityLoginBindingImpl,在步骤3.4中
return new ActivityLoginBindingImpl(component, view);
}
throw new IllegalArgumentException("The tag for activity_login is invalid. Received: " + tag);
}
//...
}
}
return null;
}
- 此时DataBinderMapperImpl和ActivityLoginBindingImpl两个对象在内存中已经存在了(提示:在研究apt生生成的代码,要知道对应的对象初始化到内存中的时机);
3.4.ActivityLoginBindingImpl
public ActivityLoginBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
//8.分析mapBindings方法
this(bindingComponent, root, mapBindings(bindingComponent, root, 6, sIncludes, sViewsWithIds));
}
//ViewDataBinding中的方法,参数3,由布局文件中控件的个数来决定
protected static Object[] mapBindings(DataBindingComponent bindingComponent, View root,
int numBindings, IncludedLayouts includes, SparseIntArray viewsWithIds) {
//8.1.创建数组
Object[] bindings = new Object[numBindings];
//8.2.将标签解析成对象,在3.5中分析
mapBindings(bindingComponent, root, bindings, includes, viewsWithIds, true);
return bindings;
}
3.5.将标签解析成对象
- 该方法的作用(知道即可,内部细节可以不用关注),将View解析出来放入到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);
}
}
}
}
- 返回到步骤8,继续分析ActivityLoginBindingImpl另一个构造方法
//ActivityLoginBindingImpl另一个构造方法
private ActivityLoginBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
super(bindingComponent, root, 2
, (cn.jack.library_weight.SubmitButton) bindings[5]
, (cn.jack.library_weight.PasswordEditText) bindings[2]
, (cn.jack.library_weight.ClearEditText) bindings[1]
, (androidx.appcompat.widget.LinearLayoutCompat) bindings[4]
, (android.widget.TextView) bindings[3]
);
this.etLoginPassword.setTag(null);
this.etLoginPhone.setTag(null);
this.mboundView0 = (androidx.appcompat.widget.LinearLayoutCompat) bindings[0];
this.mboundView0.setTag(null);
setRootTag(root);
// listeners
invalidateAll();
}
- 总结:步骤3.1-3.5,其实我们分析的就一点,当我们使用了databinding的后,就可以不需要findViewById了。因为DataBindingUtil.setContentView(this, initContentView())返回的对象ActivityLoginBinding存储了布局中View的引用;
- 使用databinding的弊端:随着项目越来越大,使用databinding会导致内存消耗越来越大,所以,大型项目是不会使用databinding,mvvm的出现主要是为了中小型项目的快速开发。重量级的大型项目(主流还是mvp,混合开发的话,一些UI使用Flutter,底层使用c和c++)是不会使用mvvm的,既要节省内存,又要有运行的快。
四.源码分析二
-
这部分我们需要探讨的是,databinding是如何做到,model的改变后,view跟着改变;
-
源码看一轮之后,不建议按照源码的执行流程去分析了,我们得换个角度,如此,才能更快的把握好主体结构;
-
databinding实际上是使用观察者模式来实现的,底层还是View.settext方案来实现View的更新;既然是观察者模式,我们就需要分清楚被观察者和观察者,同时还需要知道如何建立联系的;
4.1.被观察者
- 我们在xml中通过variable设置的type对象就是被观察者,如下,personInfo和viewModel都是观察者,我们只分析personInfo;
//activity_login.xml中的部分代码
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="personInfo"
type="cn.jack.module_login.mvvm.modle.entity.PersonInfo" />
<variable
name="viewModel"
type="cn.jack.module_login.mvvm.vm.LoginViewModel" />
</data>
<!-- 省略 -->
</layout>
- 被观察者确定好了,我们接着分析观察者
4.2.观察者
- 观察者有两个:ViewDataBinding(抽象类)和ActivityLoginBindingImpl,之所以认定这两者是观察者是因为javabean发生变化的时候,最终会触发这两者的executeBindings方法,从而实现View的更新;
public class ActivityLoginBindingImpl extends ActivityLoginBinding {
//...
@Override
public boolean setVariable(int variableId, @Nullable Object variable) {
boolean variableSet = true;
if (BR.personInfo == variableId) {
setPersonInfo((cn.jack.module_login.mvvm.modle.entity.PersonInfo) variable);
}
else if (BR.viewModel == variableId) {
setViewModel((cn.jack.module_login.mvvm.vm.LoginViewModel) variable);
}
else {
variableSet = false;
}
return variableSet;
}
public void setPersonInfo(@Nullable cn.jack.module_login.mvvm.modle.entity.PersonInfo PersonInfo) {
updateRegistration(0, PersonInfo);
this.mPersonInfo = PersonInfo;
synchronized(this) {
mDirtyFlags |= 0x1L;
}
notifyPropertyChanged(BR.personInfo);
super.requestRebind();
}
public void setViewModel(@Nullable cn.jack.module_login.mvvm.vm.LoginViewModel ViewModel) {
this.mViewModel = ViewModel;
}
@Override
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
switch (localFieldId) {
case 0 :
return onChangePersonInfo((cn.jack.module_login.mvvm.modle.entity.PersonInfo) object, fieldId);
}
return false;
}
private boolean onChangePersonInfo(cn.jack.module_login.mvvm.modle.entity.PersonInfo PersonInfo, int fieldId) {
if (fieldId == BR._all) {
synchronized(this) {
mDirtyFlags |= 0x1L;
}
return true;
}
//..
return false;
}
@Override
protected void executeBindings() {
//...
// batch finished
if ((dirtyFlags & 0x19L) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.etLoginPassword, personInfoPasswd);
}
if ((dirtyFlags & 0x10L) != 0) {
//...
androidx.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.etLoginPhone, (androidx.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.OnTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.AfterTextChanged)null, etLoginPhoneandroidTextAttrChanged);
}
if ((dirtyFlags & 0x15L) != 0) {
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.etLoginPhone, personInfoUserName);
}
}
//...
}
- 观察者也清楚了,我们现在只需要弄明白被观察者跟观察者是如何建立关联,并且被观察者发生改变,被观察者是如何通知观察者做出相应,即可明白databiding的核心原理。
4.3.被观察者和观察者建立关联
- 这个建立关联的函数为setVariable,如下:
public boolean setVariable(int variableId, @Nullable Object variable) {
boolean variableSet = true;
if (BR.personInfo == variableId) {
setPersonInfo((cn.jack.module_login.mvvm.modle.entity.PersonInfo) variable);
}
else if (BR.viewModel == variableId) {
setViewModel((cn.jack.module_login.mvvm.vm.LoginViewModel) variable);
}
else {
variableSet = false;
}
return variableSet;
}
- 比如,想要让被观察者personInfo和观察者建立关联,就需要调用ActivityLoginBinding.setVariable(BR.personInfo,personInfo)函数(或者调用setPersonInfo函数);
- 我们就从setVariable开始逐步分析被观察者和观察者之间是如何建立关联的,这里的逻辑会有些绕,因为这里的建立关联跟传统的观察者模式不一样,传统的比较简单,但是这里用了大量的类和接口才正真的建立其关联。所以,这部分的逻辑会有些赋值,不过我们慢慢分析,还是会比较清晰的。我们就以setPersonInfo作为入口。
//ActivityLoginBindingImpl类中
//1.步骤1
//---> setPersonInfo(PersonInfo)
//2.步骤2
//---> updateRegistration(0, PersonInfo);
//3.步骤3
//--->ViewDataBinding类的updateRegistration(int localFieldId, Observable observable)
//4.步骤4 得开始贴详细代码了
//--->updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER)
//4.1.先看方法的第三个参数的代码
//CREATE_PROPERTY_LISTENER是ViewDataBinding类中静态的成员变量
//是一个匿名内部类的对象,此时create方法还未被调用
private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
@Override
public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();
}
};
//4.2.updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER)
//localFieldId = 0,observable = personInfo
private boolean updateRegistration(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
//...
//mLocalFieldObservers是ViewDataBinding类的成员变量,是一个WeakListener数组
//此时,listener为null
WeakListener listener = mLocalFieldObservers[localFieldId];
if (listener == null) {
//5.步骤5
registerTo(localFieldId, observable, listenerCreator);
return true;
}
if (listener.getTarget() == observable) {
return false;
}
//略...
}
//5.步骤5
protected void registerTo(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
//...
WeakListener listener = mLocalFieldObservers[localFieldId];
//此时还是为null,进入if
if (listener == null) {
//5.给 listener 赋值为 WeakListener对象
//listenerCreator是4.1中的CREATE_PROPERTY_LISTENER
//此时,create方法调用了,WeakPropertyListener类被创建
listener = listenerCreator.create(this, localFieldId);
mLocalFieldObservers[localFieldId] = listener;
//...
}
//6.步骤6
listener.setTarget(observable);
}
//6.步骤6
//执行WeakListener类setTarget方法
private static class WeakListener<T> extends WeakReference<ViewDataBinding> {
//...
public void setTarget(T object) {
unregister();
mTarget = object;
if (mTarget != null) {
//通过上面的CREATE_PROPERTY_LISTENER我们可以知道
//mObservable = WeakPropertyListener
//mTarget = personInfo
//mObservable在这里是ObservableReference接口,故分析其具体实现类的方法
//7.步骤7:分析WeakPropertyListener的addListener
mObservable.addListener(mTarget);
}
}
//...
}
//7.步骤7
private static class WeakPropertyListener extends Observable.OnPropertyChangedCallback
implements ObservableReference<Observable> {
final WeakListener<Observable> mListener;
//...
@Override
public void addListener(Observable target) {
//target = personInfo
//调用父类Observable的方法
//8.步骤8
target.addOnPropertyChangedCallback(this);
}
//...
}
//8.步骤8
public class BaseObservable implements Observable {
//...
@Override
public void addOnPropertyChangedCallback(@NonNull OnPropertyChangedCallback callback) {
synchronized (this) {
if (mCallbacks == null) {
mCallbacks = new PropertyChangeRegistry();
}
}
//调用了mCallbacks(PropertyChangeRegistry)中的add方法。最终是将OnPropertyChangedCallback添加到一个List集合中(实际上是该接口的实现类)(需要追踪PropertyChangeRegistry父类的add方法)
//9.步骤9
mCallbacks.add(callback);
}
//...
}
//9.步骤9
//CallbackRegistry类
private List<C> mCallbacks = new ArrayList<C>();
public synchronized void add(C callback) {
if (callback == null) {
throw new IllegalArgumentException("callback cannot be null");
}
int index = mCallbacks.lastIndexOf(callback);
if (index < 0 || isRemoved(index)) {
mCallbacks.add(callback);
}
}
- 总结一下,步骤1-步骤9所作的事情,观察者ViewDataBinding中有个数组,每一个被观察者对应该数组中一个WeakListener类,WeakListener给被观察者做了一件事情,给被添加了一个接口,该接口最终是被存储到CallbackRegistry类的集合中;
- 建立关系到这里已经完成,但是我们还看不出是如何触发,触发时机上跟传统的观察者模式一样,触发的api是定义在了被观察者中。下面,我们来分析通知观察者的api。
4.4.BaseObservable类
- 通知观察者的api为BaseObservable的notifyPropertyChanged(fieldId)
- 调用PersonInfo的setUserName,会触发notifyPropertyChanged(BR.userName)(其它也类似,即Model发生变化);
- 具体一些条件判断等主体框架清楚了之后,有兴趣可以自行研究,细节上的一些判断就不添加;
//10.步骤10
public void notifyPropertyChanged(int fieldId) {
synchronized (this) {
if (mCallbacks == null) {
return;
}
}
//11.步骤11
mCallbacks.notifyCallbacks(this, fieldId, null);
}
//11.步骤11
public synchronized void notifyCallbacks(T sender, int arg, A arg2) {
mNotificationLevel++;
//12.步骤12
notifyRecurse(sender, arg, arg2);
mNotificationLevel--;
if (mNotificationLevel == 0) {
if (mRemainderRemoved != null) {
for (int i = mRemainderRemoved.length - 1; i >= 0; i--) {
final long removedBits = mRemainderRemoved[i];
if (removedBits != 0) {
removeRemovedCallbacks((i + 1) * Long.SIZE, removedBits);
mRemainderRemoved[i] = 0;
}
}
}
if (mFirst64Removed != 0) {
removeRemovedCallbacks(0, mFirst64Removed);
mFirst64Removed = 0;
}
}
}
//12.步骤12
private void notifyRecurse(T sender, int arg, A arg2) {
final int callbackCount = mCallbacks.size();
final int remainderIndex = mRemainderRemoved == null ? -1 : mRemainderRemoved.length - 1;
// Now we've got all callbakcs that have no mRemainderRemoved value, so notify the
// others.
//13.步骤13
notifyRemainder(sender, arg, arg2, remainderIndex);
// notifyRemainder notifies all at maxIndex, so we'd normally start at maxIndex + 1
// However, we must also keep track of those in mFirst64Removed, so we add 2 instead:
final int startCallbackIndex = (remainderIndex + 2) * Long.SIZE;
// The remaining have no bit set
notifyCallbacks(sender, arg, arg2, startCallbackIndex, callbackCount, 0);
}
//13.步骤13
private void notifyRemainder(T sender, int arg, A arg2, int remainderIndex) {
if (remainderIndex < 0) {
//14.步骤14
notifyFirst64(sender, arg, arg2);
} else {
final long bits = mRemainderRemoved[remainderIndex];
final int startIndex = (remainderIndex + 1) * Long.SIZE;
final int endIndex = Math.min(mCallbacks.size(), startIndex + Long.SIZE);
notifyRemainder(sender, arg, arg2, remainderIndex - 1);
notifyCallbacks(sender, arg, arg2, startIndex, endIndex, bits);
}
}
//14.步骤14
private void notifyFirst64(T sender, int arg, A arg2) {
final int maxNotified = Math.min(Long.SIZE, mCallbacks.size());
//15.步骤15
notifyCallbacks(sender, arg, arg2, 0, maxNotified, mFirst64Removed);
}
//15.步骤15
private void notifyCallbacks(T sender, int arg, A arg2, final int startIndex,
private void notifyFirst64(T sender, int arg, A arg2) {
final int maxNotified = Math.min(Long.SIZE, mCallbacks.size());
notifyCallbacks(sender, arg, arg2, 0, maxNotified, mFirst64Removed);
} final int endIndex, final long bits) {
long bitMask = 1;
for (int i = startIndex; i < endIndex; i++) {
if ((bits & bitMask) == 0) {
//调用CallbackRegistry类的NotifierCallback的实现类的onNotifyCallback
//16.步骤16
mNotifier.onNotifyCallback(mCallbacks.get(i), sender, arg, arg2);
}
bitMask <<= 1;
}
}
//16.步骤16
public abstract static class NotifierCallback<C, T, A> {
public abstract void onNotifyCallback(C callback, T sender, int arg, A arg2);
}
public class PropertyChangeRegistry extends CallbackRegistry<OnPropertyChangedCallback, Observable, Void> {
private static final NotifierCallback<OnPropertyChangedCallback, Observable, Void> NOTIFIER_CALLBACK = new NotifierCallback<OnPropertyChangedCallback, Observable, Void>() {
public void onNotifyCallback(OnPropertyChangedCallback callback, Observable sender, int arg, Void notUsed) {
//实际调用的位置
//调用Observable类中的OnPropertyChangedCallback的实现类的onPropertyChanged方法
//17.步骤17
callback.onPropertyChanged(sender, arg);
}
};
public PropertyChangeRegistry() {
super(NOTIFIER_CALLBACK);
}
public void notifyChange(@NonNull Observable observable, int propertyId) {
this.notifyCallbacks(observable, propertyId, (Object)null);
}
}
//17.步骤17
abstract class OnPropertyChangedCallback {
public abstract void onPropertyChanged(Observable sender, int propertyId);
}
private static class WeakPropertyListener extends Observable.OnPropertyChangedCallback
implements ObservableReference<Observable> {
//...
//实际调用
//17.步骤17
@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?
}
//调用handleFieldChange
//18.步骤18
binder.handleFieldChange(mListener.mLocalFieldId, sender, propertyId);
}
}
- 现在可以把步骤1-步骤18联系起来,也就是被观察者和观察者建立了关联,同时model改变后,被观察者发送通知给观察者,最终是回调了ViewDataBinding的handleFieldChange方法,我们只要分析出该方法最终的调用,看具体的操作即可;
4.5.ViewDataBinding的handleFieldChange方法
//19.步骤19
private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
//...
//onFieldChange的具体实现在ActivityLoginBindingImpl中
//内部计算是&和|,有兴趣自行研究一下,被观察者未发生变化,则不会进入if(即属性不需要更新)
boolean result = onFieldChange(mLocalFieldId, object, fieldId);
if (result) {
//分析requestRebind
//20.步骤20
requestRebind();
}
}
//20.步骤20
//条件判断略掉(避免过于深入细节)
protected void requestRebind() {
if (mContainingBinding != null) {
mContainingBinding.requestRebind();
} else {
final LifecycleOwner owner = this.mLifecycleOwner;
if (owner != null) {
Lifecycle.State state = owner.getLifecycle().getCurrentState();
if (!state.isAtLeast(Lifecycle.State.STARTED)) {
return; // wait until lifecycle owner is started
}
}
synchronized (this) {
if (mPendingRebind) {
return;
}
mPendingRebind = true;
}
if (USE_CHOREOGRAPHER) {
mChoreographer.postFrameCallback(mFrameCallback);
} else {
//会回调mRebindRunnable的run方法
//21.步骤21
mUIThreadHandler.post(mRebindRunnable);
}
}
}
//21.步骤21
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
//22.步骤22
executePendingBindings();
}
};
//22.步骤22
public void executePendingBindings() {
if (mContainingBinding == null) {
//会调用executeBindingsInternal
//23.步骤23
executeBindingsInternal();
} else {
mContainingBinding.executePendingBindings();
}
}
//23.步骤23
private void executeBindingsInternal() {
//...
if (!mRebindHalted) {
//抽象方法,具体实现在在ActivityLoginBindingImpl中查看
//24.步骤24
executeBindings();
//...
}
//...
}
//24.步骤24
//ActivityLoginBindingImpl类
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
java.lang.String personInfoPasswd = null;
cn.jack.module_login.mvvm.modle.PersonInfo personInfo = mPersonInfo;
java.lang.String personInfoUserName = null;
if ((dirtyFlags & 0x1dL) != 0) {
if ((dirtyFlags & 0x19L) != 0) {
if (personInfo != null) {
// read personInfo.passwd
personInfoPasswd = personInfo.getPasswd();
}
}
if ((dirtyFlags & 0x15L) != 0) {
if (personInfo != null) {
// read personInfo.userName
personInfoUserName = personInfo.getUserName();
}
}
}
// batch finished
if ((dirtyFlags & 0x19L) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.etLoginPassword, personInfoPasswd);
}
if ((dirtyFlags & 0x10L) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.etLoginPassword, (androidx.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.OnTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.AfterTextChanged)null, etLoginPasswordandroidTextAttrChanged);
androidx.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.etLoginPhone, (androidx.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.OnTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.AfterTextChanged)null, etLoginPhoneandroidTextAttrChanged);
}
if ((dirtyFlags & 0x15L) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.etLoginPhone, personInfoUserName);
}
}
//TextViewBindingAdapter.setText最终还是View.setText
- 到这里,databinding的核心原理分析完毕。
4.6.总结
- 为了避免看源码引起不适,重新把被观察者数据发生改变后是如何通知观察者的做个整理,如下:
被观察者持有CallbackRegistry类的引用,在被观察者数据发生变化的时候,通过调用
CallbackRegistry的方法
--->PropertyChangeRegistry(CallbackRegistry的实现类)的方法
--->OnPropertyChangedCallback的onPropertyChanged方法
--->WeakPropertyListener(OnPropertyChangedCallback的实现类)的onPropertyChanged(WeakPropertyListener是ViewDataBinding的静态内部类)
--->ViewDataBinding类的handleFieldChange方法
--->ViewDataBinding类的requestRebind方法
--->ViewDataBinding类的mRebindRunnable的run方法
--->ViewDataBinding类的executePendingBindings方法
--->ViewDataBinding类executeBindingsInternal方法
--->ViewDataBinding类executeBindings抽象方法(需要找实现类)
--->ActivityLoginBindingImpl类executeBindings方法
4.7.补充两个细节
- mDirtyFlags与BR之间的计算,是通过&和|来计算的,BR中的变量对应的值在计算中是2的n次方
public class BR {
public static int _all = 0;
public static int data = 1;
public static int passwd = 2;
public static int personInfo = 3;
public static int userName = 4;
public static int viewModel = 5;
}
//ActivityLoginBindingImpl类
//mDirtyFlags存储的信息来判断哪个变量修改了
private boolean onChangePersonInfo(cn.jack.module_login.mvvm.modle.PersonInfo PersonInfo, int fieldId) {
if (fieldId == BR._all) {
synchronized(this) {
mDirtyFlags |= 0x1L; //2^0 BR._all = 0
}
return true;
}
else if (fieldId == BR.userName) {
synchronized(this) {
mDirtyFlags |= 0x4L; //2^1 BR.userName = 1
}
return true;
}
else if (fieldId == BR.passwd) {
synchronized(this) {
mDirtyFlags |= 0x8L; //2^2 BR.passwd = 2
}
return true;
}
return false;
}
- 初始的数据是如何获取的,ViewDataBinding的静态代码块初始化的位置,有设置回调。当View绑定到窗口的时候 onViewAttachedToWindow 自动触发该回调(framework层调用),从而执行了mRebindRunnable的run方法,最终也是调用ActivityLoginBindingImpl类executeBindings方法;
评论区