layout布局在编译过程生成的重要文件(以activity_main.xml为例):

1、app\build\intermediates\data_binding_layout_info_type_package目录的activity_main-layout.xml,主要处理数据部分,记录节点信息,生成tag,twoway是记录是否双向绑定,截取部分代码

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Layout directory="layout" filePath="app\src\main\res\layout\activity_main.xml"isBindingData="true" isMerge="false" layout="activity_second"modulePackage="com.example.myapplication" rootNodeType="android.widget.LinearLayout"><Variables name="user" declared="true" type="com.example.myapplication.User"><location endLine="8" endOffset="51" startLine="6" startOffset="8" /></Variables><Targets><Target tag="layout/activity_main_0" view="LinearLayout"><Expressions /><location endLine="28" endOffset="18" startLine="12" startOffset="4" /></Target><Target id="@+id/idName" tag="binding_1" view="TextView"><Expressions><Expression attribute="android:text" text="user.name"><Location endLine="21" endOffset="38" startLine="21" startOffset="12" /><TwoWay>false</TwoWay><ValueLocation endLine="21" endOffset="36" startLine="21" startOffset="28" /></Expression></Expressions><location endLine="21" endOffset="40" startLine="17" startOffset="8" /></Target></Targets>
</Layout>

2、appbuild\generated\data_binding_base_class_source_out下的ActivityMainBinding.java,View和Model的实例

// Generated by data binding compiler. Do not edit!
package com.example.myapplication.databinding;public abstract class ActivityMainBinding extends ViewDataBinding {...省略部分@NonNullpublic final TextView idName;@Bindableprotected User mUser;protected ActivityMainBinding(Object _bindingComponent, View _root,int _localFieldCount, LinearLayout llId, NavigatorBar navigatorBar,TextView idName ) {super(_bindingComponent, _root, _localFieldCount);this.llId = llId;this.navigatorBar = navigatorBar;this.idName = idName;}public abstract void setUser(@Nullable User user);@Nullablepublic User getUser() {return mUser;}@NonNullpublic static ActivityMainBinding inflate(@NonNull LayoutInflater inflater, @Nullable ViewGroup root, boolean attachToRoot) {return inflate(inflater, root, attachToRoot, DataBindingUtil.getDefaultComponent());}/***  省略一些 @Deprecated 方法*/@NonNullpublic static ActivityMainBinding inflate(@NonNull LayoutInflater inflater) {return inflate(inflater, DataBindingUtil.getDefaultComponent());}public static ActivityMainBinding bind(@NonNull View view) {return bind(view, DataBindingUtil.getDefaultComponent());}
}

3、app\build\generated\ap_generated_sources下的BR.java。当数据改变后,可以用该标识符通知 DataBinding,用新的数据去更新UI。

public class BR {public static int _all = 0;public static int user = 1;
}

下面分析一下源代码

通过layout布局映射获取相应的Binding。

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);}
}

进入DataBindingUtil中看一下 ,发现DataBindingUtil.setContentView其实落脚点也是activity.setContentView。(针对自定义view,也提供了inflate方法,可以去设置layout)。代码按调用顺序调整了一下,

/*** Utility class to create {@link ViewDataBinding} from layouts.*/
public class DataBindingUtil {private static DataBinderMapper sMapper = new DataBinderMapperImpl();private static DataBindingComponent sDefaultComponent = null;public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity,int layoutId) {return setContentView(activity, layoutId, sDefaultComponent);}public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity,int layoutId, @Nullable DataBindingComponent bindingComponent) {
//落脚点也是activity.setContentViewactivity.setContentView(layoutId);
//各类布局源码最终根节点id都是android.R.id.content,所以这里是获取根节点id的viewView decorView = activity.getWindow().getDecorView();ViewGroup contentView = (ViewGroup) decorView.findViewById(android.R.id.content);return bindToAddedViews(bindingComponent, contentView, 0, layoutId);}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) {//如果仅有一个子Viewfinal View childView = parent.getChildAt(endChildren - 1);return bind(component, childView, layoutId);} else {
//如果有duo个子Viewfinal View[] children = new View[childrenAdded];for (int i = 0; i < childrenAdded; i++) {children[i] = parent.getChildAt(i + startChildren);}return bind(component, children, layoutId);}}
}

进入DataBindingUtil类中的bind方法看一下

    @SuppressWarnings("unchecked")static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View[] roots,int layoutId) {return (T) sMapper.getDataBinder(bindingComponent, roots, layoutId);}static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View root,int layoutId) {return (T) sMapper.getDataBinder(bindingComponent, root, layoutId);}

关注到sMapper。是个静态成员变量,也就是类被调用时便开始初始化了

public class DataBindingUtil {private static DataBinderMapper sMapper = new DataBinderMapperImpl();private static DataBindingComponent sDefaultComponent = null;
}/**
*这个类在编译时动态生成的,看到里面还有一个DataBinderMapperImpl,待会儿说一下
*/
public class DataBinderMapperImpl extends MergedDataBinderMapper {DataBinderMapperImpl() {  //调用的是父类的addMapper方法//注意这里添加的并不是自身,而是下面要说的另一个同名的实现类addMapper(new com.example.myapplication.DataBinderMapperImpl());}
}public class MergedDataBinderMapper extends DataBinderMapper public void addMapper(DataBinderMapper mapper) {Class<? extends DataBinderMapper> mapperClass = mapper.getClass();// 如果之前不存在,则加入 mExistingMappersif (mExistingMappers.add(mapperClass)) {mMappers.add(mapper);//获取该 mapper 所依赖的其它 DataBinderMapper,也加进去final List<DataBinderMapper> dependencies = mapper.collectDependencies();for(DataBinderMapper dependency : dependencies) {//将依赖的其他DataBinderMapper也添加进去addMapper(dependency);}}}
}

addMaper方法是将生成的DataBinderMapperImpl以及它所依赖的其他DataBinderMapper加入到mExistingMappers和mMappers集合中。下面进入com.example.myapplication包下的DataBinderMapperImpl的getDataBinder看看

package com.example.myapplication;public class DataBinderMapperImpl extends DataBinderMapper {private static final int LAYOUT_ACTIVITYSTARTDETAIL = 1;private static final int LAYOUT_ACTIVITYMAIN = 2;private static final SparseIntArray INTERNAL_LAYOUT_ID_LOOKUP = new SparseIntArray(2);static {
//将databinding的布局文件和类定义的常量对应INTERNAL_LAYOUT_ID_LOOKUP.put(com.example.myapplication.R.layout.activity_start_detail, LAYOUT_ACTIVITYSTARTDETAIL);INTERNAL_LAYOUT_ID_LOOKUP.put(com.example.myapplication.R.layout.activity_main, LAYOUT_ACTIVITYMAIN);}@Overridepublic ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) {
//根据layout布局文件的id获取自定义的idint localizedLayoutId = INTERNAL_LAYOUT_ID_LOOKUP.get(layoutId);if(localizedLayoutId > 0) {
//获取文件的tag,前面有提到编译时自动生成的tagfinal Object tag = view.getTag();if(tag == null) {throw new RuntimeException("view must have a tag");}
//根据tag加载对应的bindingImplswitch(localizedLayoutId) {case  LAYOUT_ACTIVITYSTARTDETAIL: {if ("layout/activity_start_detail_0".equals(tag)) {return new ActivityStartDetailBindingImpl(component, view);}throw new IllegalArgumentException("The tag for activity_start_detail is invalid. Received: " + tag);}case  LAYOUT_ACTIVITYMAIN: {if ("layout/activity_main_0".equals(tag)) {return new ActivityMainBindingImpl(component, view);}throw new IllegalArgumentException("The tag for activity_main is invalid. Received: " + tag);}}}return null;}@Overridepublic List<DataBinderMapper> collectDependencies() {ArrayList<DataBinderMapper> result = new ArrayList<DataBinderMapper>(8);result.add(new androidx.databinding.library.baseAdapters.DataBinderMapperImpl());return result;}
}

所以DataBindingUtil.setContentView方法最后返回的是根据layout文件在编译期间生成的对应的ViewBinding的实现类(比如activity_main.xml对应的ActivityMainBindingImpl),看下这个类进入构造方法

public class ActivityMainBindingImpl extends ActivityMainBinding  {@Nullableprivate static final androidx.databinding.ViewDataBinding.IncludedLayouts sIncludes;@Nullableprivate static final android.util.SparseIntArray sViewsWithIds;static {sIncludes = null;sViewsWithIds = new android.util.SparseIntArray();...sViewsWithIds.put(R.id.idName, 5);}public ActivityMainBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {this(bindingComponent, root, mapBindings(bindingComponent, root, 6, sIncludes, sViewsWithIds));}private ActivityStartDetailBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {super(XXXXX);//将tag置空this.mboundView0 = (android.widget.LinearLayout) bindings[0];this.mboundView0.setTag(null);setRootTag(root);// listenersinvalidateAll();}@Overridepublic void invalidateAll() {synchronized(this) {mDirtyFlags = 0x1L;}requestRebind();}
}

第一个构造方法中调用了父类ViewDataBinding的mapBindings方法

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")) {// 判断是否是根布局, 根布局的 tag = layout/activity_xxx_数字             final int underscoreIndex = tag.lastIndexOf('_');if (underscoreIndex > 0 && isNumeric(tag, underscoreIndex + 1)) {//以tag后的数字为下标,将对应tag的View放进指定下标的数组位置中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_ 开头
//以tag后的数字为下标,将对应tag的View放进指定下标的数组位置中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 viewindexInIncludes = -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++) {// 遍历 ViewGroup 的 childrenfinal View child = viewGroup.getChildAt(i);//是否为include布局的标记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) {//对include布局 进行处理// 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);}}}}

我们可以知道mapBindings方法返回bindings数组,数组中存放的就是布局文件中的View,而数组下标以生成的tag中的数字作为View在bindings数组中对应的下标。现在可以回到构造方法里面往下看。

 public ActivityStartDetailBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {this(bindingComponent, root, mapBindings(bindingComponent, root, 6, sIncludes, sViewsWithIds));}private ActivityStartDetailBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {super(bindingComponent, root, 0, (android.widget.Button) bindings[5], (android.widget.FrameLayout) bindings[3], (com.sf.trtms.lib.widget.NavigatorBar) bindings[1], (androidx.recyclerview.widget.RecyclerView) bindings[4], (android.view.View) bindings[2]);//将tag置空this.mboundView0 = (android.widget.LinearLayout) bindings[0];this.mboundView0.setTag(null);setRootTag(root);// listenersinvalidateAll();}

ActivityStartDetailBindingImpl调用了ActivityStartDetailBinding的构造方法,ActivityStartDetailBinding的构造方法又调用了ViewDataBinding的构造方法。

protected ViewDataBinding(DataBindingComponent bindingComponent, View root, int localFieldCount) {mBindingComponent = bindingComponent;mLocalFieldObservers = new WeakListener[localFieldCount];this.mRoot = root;if (Looper.myLooper() == null) {throw new IllegalStateException("DataBinding must be created in view's UI Thread");}if (USE_CHOREOGRAPHER) {//设备系统  API不低于16//进行mChoreographer初始化以及mFrameCallback的具体实现mChoreographer = Choreographer.getInstance();mFrameCallback = new Choreographer.FrameCallback() {@Overridepublic void doFrame(long frameTimeNanos) {mRebindRunnable.run();}};} else {//绑定至主线程的HandlermFrameCallback = null;mUIThreadHandler = new Handler(Looper.myLooper());}}

(Choreographer是UI渲染的重要类,后面在性能优化里面加一篇讲解一下)。无论api为多少,当收到消息时最终都会执行mRebindRunnable.run();这是后话,我们回到ActivityStartDetailBindingImpl的构造方法继续往下走invalidateAll()方法。

public void invalidateAll() {synchronized(this) {mDirtyFlags = 0x8L;}requestRebind();}/** ViewDataBinding的方法**/
protected void requestRebind() {if (mContainingBinding != null) {mContainingBinding.requestRebind();} else {final LifecycleOwner owner = this.mLifecycleOwner;if (owner != null) {//判断该生命周期持有者是否为活跃状态(Started和resume)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) {//API不低于16,就用 mChoreographer 来渲染//在上面的ViewDataBinding构造函数中mFrameCallback最终调用了mRebindRunnablemChoreographer.postFrameCallback(mFrameCallback);} else {//使用UI主线程的 Handler执行RunnablemUIThreadHandler.post(mRebindRunnable);}         }}

不管api多少最后都会执行mRebindRunnable的run方法

private final Runnable mRebindRunnable = new Runnable() {@Overridepublic 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 IntelliJif (!mRoot.isAttachedToWindow()) {// Don't execute the pending bindings until the View// is attached again.//view没attach到window是移除监听mRoot.removeOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);mRoot.addOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);return;}}executePendingBindings();}};public void executePendingBindings() {if (mContainingBinding == null) {executeBindingsInternal();} else {mContainingBinding.executePendingBindings();}}private void executeBindingsInternal() {...判断数据合规性情形mIsExecutingPendingBindings = true;mRebindHalted = false;if (mRebindCallbacks != null) {mRebindCallbacks.notifyCallbacks(this, REBIND, null);// The onRebindListeners will change mPendingHaltedif (mRebindHalted) {mRebindCallbacks.notifyCallbacks(this, HALTED, null);}}if (!mRebindHalted) {executeBindings();if (mRebindCallbacks != null) {mRebindCallbacks.notifyCallbacks(this, REBOUND, null);}}mIsExecutingPendingBindings = false;}

如果存在最后调用的是ActivityMainBindingImpl的executeBindings()

protected void executeBindings() {  long dirtyFlags = 0;synchronized(this) {dirtyFlags = mDirtyFlags;mDirtyFlags = 0;}   ...
androidx.databinding.ObservableField<java.lang.String> userName = null;com.example.myapplication.User user = mUser;java.lang.String userNameGet = null;if ((dirtyFlags & 0xfL) != 0) {if ((dirtyFlags & 0xdL) != 0) {if (user != null) {// read user.nameuserName = user.getName();}//注意这个0是自动生成的localFieldId,以后用得上 =。=updateRegistration(0, userName);if (userName != null) {// read user.name.get()userNameGet = userName.get();}}...}// 根据不同标志位(编译时动态决定),刷新 UI 控件       if ((dirtyFlags & 0xdL) != 0) {//在这里将数据进行了赋值,根据@+id中定义的id名定义控件androidx.databinding.adapters.TextViewBindingAdapter.setText(this.idName, userNameGet);}}// dirty flagprivate  long mDirtyFlags = 0xffffffffffffffffL;/* flag mappingflag 0 (0x1L): user.nameflag 1 (0x2L): userflag 2 (0x3L): nullflag mapping end*///end

总结

  • 编译过程中会生成带有tag的布局文件(根布局layout/XXX_0,其他binding_数字)
  • DataBindingUtil.setContentView根据编译期间新生成的布局文件的tag得到相应布局的ViewBinding的实现类
  • 将布局的子View,全部存放在数组中,然后将数组中的View赋值给生成的ViewBinding的属性,这样就可以直接使用生成的ViewBinding的属性来使用view,无需再调用findViewById了。bindings[index] = view。数据的下标就是根据tag的数字生成的
  • 根据不同标志位(编译时动态决定),刷新相应的 UI 控件。

mDirtyFlags标记位非常重要,如果使用了ObservableField等用于自动刷新的类,则在标记位就会包含相应的映射,如果使用的是String、Int等数据类型,则不会包含。

动态设置数据绑定有2种方式:

第一种:实体类继承BaseObservable,或者自己实现Observable;使用ObservableField<>,泛型可以填入自己需要的类型,注意必须要初始化。对于基本数据类型也可以直接使用ObservableBoolean, ObservableByte, ObservableChar, ObservableShort, ObservableInt, ObservableLong, ObservableFloat, ObservableDouble和ObservableParcelable;属性设为public。

第二种:实体类继承BaseObservable,或者自己实现Observable;在需要刷新的属性的get方法上添加@Bindable注解,此时会自动生成BR类(很多时候BR文件不会自动生成,那就重启AS吧...);在相应的set方法里调用notifyPropertyChanged(BR.xxx)进行刷新。

上面是第一种方式的executeBindings,我们看看第二种方式的

protected void executeBindings() {long dirtyFlags = 0;synchronized(this) {dirtyFlags = mDirtyFlags;mDirtyFlags = 0;}java.lang.String userName = null;com.example.myapplication.User user = mUser;if ((dirtyFlags & 0x3L) != 0) {if (user != null) {// read user.nameuserName = user.getName();...}               }// batch finishedif ((dirtyFlags & 0x3L) != 0) {// api target 1androidx.databinding.adapters.TextViewBindingAdapter.setText(this.idName, userName);...}}private  long mDirtyFlags = 0xffffffffffffffffL;/* flag mappingflag 0 (0x1L): userflag 1 (0x2L): nullflag mapping end*/

mDirtyFlags的映射也发生了改变。对比两种情况,除了标志位的变化之后还有在进行属性赋值的时候,如果是ObservableField类型的数据都会将属性值作为参数去调用updateRegistration()方法

protected boolean updateRegistration(int localFieldId, Observable observable) {return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
}
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;
} //属性监听类,还有其他类型CREATE_LIST_LISTENER、CREATE_MAP_LISTENER、
//CREATE_LIVE_DATA_LISTENER
private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {@Overridepublic WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();}
}; 

进入registerTo方法,看一下具体实现:

protected void registerTo(int localFieldId, Object observable,CreateWeakListener listenerCreator) {if (observable == null) {return;}WeakListener listener = mLocalFieldObservers[localFieldId];if (listener == null) {//listenerCreator.create会创建一个WeakListener对象//通过构造方法创建WeakPropertyListener对象,又通过getListener返回WeakListener对象listener = listenerCreator.create(this, localFieldId);mLocalFieldObservers[localFieldId] = listener;if (mLifecycleOwner != null) {listener.setLifecycleOwner(mLifecycleOwner);}}listener.setTarget(observable);}

进入setTarget()

public void setTarget(T object) {unregister();
//mTarget就是ObservableField对象mTarget = object;if (mTarget != null) {//mObservable是在使用构造函数的时候进行创建的,所以实际上是一个WeakPropertyListener类型//mListener = new WeakListener<Observable>(binder, localFieldId, this);mObservable.addListener(mTarget);}}//添加属性改变监听public void addListener(Observable target) {target.addOnPropertyChangedCallback(this);}

addListener()方法中的target就是ObservableField对象,它是一个被观察者。通过addOnPropertyChangedCallback()方法,相当于向target注册一个观察者

public interface Observable {void addOnPropertyChangedCallback(OnPropertyChangedCallback callback);void removeOnPropertyChangedCallback(OnPropertyChangedCallback callback);abstract class OnPropertyChangedCallback {/*** Called by an Observable whenever an observable property changes.* @param sender The Observable that is changing.* @param propertyId The BR identifier of the property that has changed. The getter*                   for this property should be annotated with {@link Bindable}.*/public abstract void onPropertyChanged(Observable sender, int propertyId);}
}

在WeakPropertyListener中的具体实现:

private static class WeakPropertyListener extends Observable.OnPropertyChangedCallbackimplements ObservableReference<Observable> {final WeakListener<Observable> mListener;public WeakPropertyListener(ViewDataBinding binder, int localFieldId) {mListener = new WeakListener<Observable>(binder, localFieldId, this);}...@Overridepublic 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 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) {
//与前面过程相同,最终更新UI的数据。requestRebind();}}

onFieldChange用于判断数据是否发生了更改,看一下在ActivityMainBindingImpl中的具体实现:

protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {switch (localFieldId) {//localFieldId是在executeBindings方法里面自动生成,从updateRegistration一步一步传过来的case 0 :return onChangeUserName((androidx.databinding.ObservableField<java.lang.String>) object, fieldId);...}return false;}private boolean onChangeUserName(androidx.databinding.ObservableField<java.lang.String> UserName, int fieldId) {if (fieldId == BR._all) {synchronized(this) {//根据前面的标志位映射的注释可知,这个方法用来判断user.name是否发生了变化//flag 0 (0x1L): user.namemDirtyFlags |= 0x1L;}return true;}return false;}

总结

当数据源发生变化,会回调WeakPropertyListener的onPropertyChanged()方法。在WeakPropertyListener中持有ViewDataBinding对象,调用该对象的handleFieldChange()方法,去更新View。

到这,DataBinding的启动部分分析完了。

反向绑定
双向绑定意思不仅数据绑定UI,同时UI更新时可以刷新数据,语法为@={}。我们看下反向绑定的过程。CheckBox 继承 CompoundButton我们用CompoundButton的checked属性结合CompoundButtonBindingAdapter源码来说明反向绑定的过程。

@BindingMethods({@BindingMethod(type = CompoundButton.class, attribute = "android:buttonTint", method = "setButtonTintList"),@BindingMethod(type = CompoundButton.class, attribute = "android:onCheckedChanged", method = "setOnCheckedChangeListener"),
})
@InverseBindingMethods({@InverseBindingMethod(type = CompoundButton.class, attribute = "android:checked"),
})
public class CompoundButtonBindingAdapter {@BindingAdapter("android:checked")public static void setChecked(CompoundButton view, boolean checked) {if (view.isChecked() != checked) {view.setChecked(checked);}}@BindingAdapter(value = {"android:onCheckedChanged", "android:checkedAttrChanged"},requireAll = false)public static void setListeners(CompoundButton view, final OnCheckedChangeListener listener,final InverseBindingListener attrChange) {if (attrChange == null) {view.setOnCheckedChangeListener(listener);} else {view.setOnCheckedChangeListener(new OnCheckedChangeListener() {@Overridepublic void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {if (listener != null) {listener.onCheckedChanged(buttonView, isChecked);}attrChange.onChange();}});}}
}

@InverseBindingMethods:元素为@InverseBindingMethod的一个数组,用来注解 类 。
@InverseBindingMethod:反向绑定方法,用来确定怎么去监听view属性的变化和回调哪一个getter方法。包含以下4个属性:

  • type:包含attribute的view类型。
  • attribute:支持双向绑定的属性(string格式)。
  • event:可以省略,用来通知DataBinding系统attribute已经改变,默认为attribute + "AttrChanged"。(UI通知数据)
  • method:可以省略,用来从view获取数据的方法,不设定就会自动寻找"is" 或 "get"+attribute方法。

@InverseBindingMethod(type = CompoundButton.class, attribute = "android:checked")省略了事件event(checkedAttrChanged)和method(isChecked)

@BindingAdapter注解设置事件调用时机

@BindingAdapter("android:checked")

checkbox中使用checked属性时,会自动调用setChecked方法.。

我们在layout中使用双向绑定

        <CheckBoxandroid:id="@+id/checkbox"android:layout_width="wrap_content"android:layout_height="wrap_content"android:checked="@={user.checked}"/>

当Checked属性变化时触发事件checkeAttrChanged,通过bindingAdapter注解调用setListeners,使用了InverseBindingListener接口以供用户将事件通知抛出。InverseBindingListener接口具体实现代码由框架生成,总得来说就是获取控件属性的当前值,然后用此值更新数据。

InverseBindingListener是个抽象接口,有一个onChange方法,看下layout的binding类中自动生成的InverseBindingListener实现。 ​

private android.databinding.InverseBindingListener checkboxandroidCheck = new android.databinding.InverseBindingListener() {@Overridepublic void onChange() {
//这段逻辑其实就是用来更新user实体类中的checked字段的// Inverse of user.checked.get()//         is user.checked.set((java.lang.Boolean) callbackArg_0)boolean callbackArg_0 = checkbox.isChecked();//其实就是method// localize variables for thread safety// user.checked != nullboolean checkedUserObjectnul = false;// user.checkedandroid.databinding.ObservableField<java.lang.Boolean> checkedUser = null;// usercom.example.myapplication.User user = mUser;// user.checked.get()java.lang.Boolean CheckedUser1 = null;// user != nullboolean userObjectnull = false;userObjectnull = (user) != (null);if (userObjectnull) {checkedUser = user.checked;checkedUserObjectnul = (checkedUser) != (null);if (checkedUserObjectnul) {checkedUser.set((java.lang.Boolean) (callbackArg_0));}}}};

总结

整个反向绑定的流程下来其实就是:

  1. @InverseBindingMethod(type = CompoundButton.class, attribute = "android:checked",event="checkedAttrChanged",method="isChecked" )                                   定义需要反向绑定的属性(checked),并配置event(checkedAttrChanged)和method(isChecked)。
  2. @BindingAdapter(value = {"android:onCheckedChanged","android:checkedAttrChanged"},requireAll = false)  系统会自动根据event找到对应的方法(setListeners),配置好调用时机。setListeners中调用InverseBindingListener.onchange()
  3. 在layout中使用双向绑定android:checked="@={user.checked}。
  4. 自动在binding类中生成一个InverseBindingListener实现。回调方法onChange中刷新数据

查看TextViewBindingAdapter发现还有未讲到的点

@InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged")public static String getTextString(TextView view) {return view.getText().toString();}

这个注解的方法就相当于作用在view上获取数据的getter方法。

  • attribute:支持双向绑定的属性(string格式)。
  • event:可以省略,用来通知DataBinding系统attribute已经改变的事件,默认为attribute + "AttrChanged"。需要通过@BindingAdapter进行设置调用时机。

思考:

1、事件驱动反向绑定成功后,UI改变数据也会发生变化,按正常逻辑来讲,将继续触发单向绑定,为什么不会陷入无限循环中呢?

为中断这种循环,通常的做法是在更新UI前,校验新旧数据是否相同,如果相同则不进行刷新动作。比如TextView,其内部的setText方法并不会检验新旧数据的一致性问题,所以在TextViewBindingAdapter内重新绑定了android:text属性,添加校验逻辑。

@BindingAdapter("android:text")
public static void setText(TextView view, CharSequence text) {//校验final CharSequence oldText = view.getText();if (text == oldText || (text == null && oldText.length() == 0)) {return;}...view.setText(text);
}

2、DataBinding与ButterKnife的区别

ButterKnife插件采用注解的方式来查找控件和注册监听,减少重复代码,但还是不够简洁:比如给TextView设置文案必须调用setText();要获取editText的内容前必须拿到editText对象;给view设置监听前也必须要获取这个view对象等。但是使用了DataBinding之后,这些冗余统统可以得到简化,从而只需要专注于业务逻辑了。

------------------------------欢迎多多批评指正

DataBinding源码分析相关推荐

  1. 【Android CameraX】CameraXBasic —— 官方CameraX实例源码分析

    一.简介 二.源码分析 2.1 build.gradle 2.2 代码结构 2.3 变量 2.3.1 lensFacing 2.3.2 preview 2.3.3 Image capture 2.3. ...

  2. 【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析

    目录[阅读时间:约10分钟] 一.概述 二.对比: gorilla/mux与net/http DefaultServeMux 三.简单使用 四.源码简析 1.NewRouter函数 2.HandleF ...

  3. SpringBoot-web开发(四): SpringMVC的拓展、接管(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) SpringBoot-web开发(二): 页面和图标定制(源码分析) SpringBo ...

  4. SpringBoot-web开发(二): 页面和图标定制(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) 目录 一.首页 1. 源码分析 2. 访问首页测试 二.动态页面 1. 动态资源目录t ...

  5. SpringBoot-web开发(一): 静态资源的导入(源码分析)

    目录 方式一:通过WebJars 1. 什么是webjars? 2. webjars的使用 3. webjars结构 4. 解析源码 5. 测试访问 方式二:放入静态资源目录 1. 源码分析 2. 测 ...

  6. Yolov3Yolov4网络结构与源码分析

    Yolov3&Yolov4网络结构与源码分析 从2018年Yolov3年提出的两年后,在原作者声名放弃更新Yolo算法后,俄罗斯的Alexey大神扛起了Yolov4的大旗. 文章目录 论文汇总 ...

  7. ViewGroup的Touch事件分发(源码分析)

    Android中Touch事件的分发又分为View和ViewGroup的事件分发,View的touch事件分发相对比较简单,可参考 View的Touch事件分发(一.初步了解) View的Touch事 ...

  8. View的Touch事件分发(二.源码分析)

    Android中Touch事件的分发又分为View和ViewGroup的事件分发,先来看简单的View的touch事件分发. 主要分析View的dispatchTouchEvent()方法和onTou ...

  9. MyBatis原理分析之四:一次SQL查询的源码分析

    上回我们讲到Mybatis加载相关的配置文件进行初始化,这回我们讲一下一次SQL查询怎么进行的. 准备工作 Mybatis完成一次SQL查询需要使用的代码如下: Java代码   String res ...

最新文章

  1. 想“看见”高性能计算嘛?戳这里开始
  2. Win10中SVN图标不显示的解决
  3. 【Android-tips】 Unable to execute dex: Multiple dex files define 解决方法
  4. java实现二进制转十六进制
  5. Dev C++,一个好玩的猜数字游戏
  6. div 设置a4大小_如何在A4纸张尺寸页面制作HTML页面?
  7. 面试题:如何设计一个高并发的系统?
  8. centos mysql无法启动 sock_【零基础学云计算】MYSQL的主从复制、读写分离
  9. 区间DP{环形}:石子归并-2
  10. listview-android:打造万能通用适配器(转)
  11. Uninstalling ASP.NET MVC 1.1 after installing Visual Studio 2010 beta 2
  12. 爱立信卫翰思:已囊括拉美一半以上…
  13. 手机浏览器自动播放视频video(设置autoplay无效)的解决方案
  14. 【图像增强】基于matlab HSI+同态滤波彩色图像增强【含Matlab源码 1515期】
  15. 360全景倒车影像2017年最新十大品牌排名
  16. linux这么重命名文件,如何在Linux中重命名文件
  17. linux界面任务栏平铺,可能是linux 4.9.8的问题-安装15.4后桌面没有任务栏
  18. 解决linux“嘟嘟”的报警声
  19. 【编译原理】Python实现对一个英文文本的词频统计
  20. 北京19家A类定点医院名单

热门文章

  1. mysql将查询结果作为临时表查询_mysql使用查询结果作为临时表
  2. 图标设计原则_图标设计的7个原则
  3. 微信小程序App Page 模块化
  4. mysql函数中打印信息_mysql信息函数
  5. Trister‘s Lend告诉你如何选对借贷平台
  6. Draggable拖拽+Collapse使用(不一样的手风琴)
  7. ArcGIS栅格计算器求2个栅格数据的交集(区域)
  8. 关于hive异常:Unable to instantiate org.apache.hadoop.hive.ql.metadata.SessionHiveMetaStor
  9. 画部分点之间的连线图
  10. Verilog设计流程:综合(一)