一、前言
在编译阶段,DataBinding会介入,扫描所有 res/layout/ 下的所有布局文件,然后为其生成相应的 ViewBinding 抽象类和实现类。
注:<androidx.databinding:databinding-compiler:版本号>与 <com.android.tools.build:gradle:版本号>一致
我们一般会在 Activity.onCreate 方法中,使用 DataBindingUtil.setContentView 来返回 ViewBinding 对象实例:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(
this, R.layout.activity_main)
}
}
ActivityMainBinding 其实就是 layout 的 ViewBinding 类,它是通过 APT / KAPT 注解生成出来的抽象类,通常在
build/generated/data_binding_base_class_source_out/<环境>/dataBindingGenBaseClassesDebug/out/<包名>/databinding/......
- 例如:ActivityMainBinding
public abstract class ActivityMainBinding extends ViewDataBinding {
// layout.xml 中有多少控件,这里就会列举出来,无论是否需要绑定 model 中的属性
@NonNull
public final EditText name;
@NonNull
public final EditText pswd;
@NonNull
public final TextView text;
@Bindable
protected User mUser; // 绑定的 Model
protected ActivityMainBinding(Object _bindingComponent, View _root, int _localFieldCount,
EditText name, EditText pswd, TextView text) {
super(_bindingComponent, _root, _localFieldCount);
this.name = name;
this.pswd = pswd;
this.text = text;
}
public abstract void setUser(@Nullable User user);
@Nullable
public User getUser() {
return mUser;
}
@NonNull
public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater,
@Nullable ViewGroup root, boolean attachToRoot) {
return inflate(inflater, root, attachToRoot, DataBindingUtil.getDefaultComponent());
}
// 过滤掉 Deprecated 代码
......
@NonNull
public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater) {
return inflate(inflater, DataBindingUtil.getDefaultComponent());
}
public static ActivityMainBinding bind(@NonNull View view) {
return bind(view, DataBindingUtil.getDefaultComponent());
}
// 过滤掉 Deprecated 代码
......
}
ActivityMainBinding 的实现类在
build/generated/source/kapt/<环境>/<包名>/databinding/......
- ActivityMainBindingImpl
import <包名>.BR; // build/generated/source/kapt/<环境>/<包名>/BR.java
public class ActivityMainBindingImpl extends ActivityMainBinding {
@Nullable
private static final androidx.databinding.ViewDataBinding.IncludedLayouts sIncludes;
@Nullable
private static final android.util.SparseIntArray sViewsWithIds;
static {
sIncludes = null;
sViewsWithIds = null;
}
// views
@NonNull
private final androidx.constraintlayout.widget.ConstraintLayout mboundView0;
// variables
// values
// listeners
// Inverse Binding Event Handlers
private androidx.databinding.InverseBindingListener nameandroidTextAttrChanged = new androidx.databinding.InverseBindingListener() {
@Override
public void onChange() {
// Inverse of user.name
// is user.setName((java.lang.String) callbackArg_0)
java.lang.String callbackArg_0 = androidx.databinding.adapters.TextViewBindingAdapter.getTextString(name);
// localize variables for thread safety
// user.name
java.lang.String userName = null;
// user != null
boolean userJavaLangObjectNull = false;
// user
com.demo.study.model.User user = mUser;
userJavaLangObjectNull = (user) != (null);
if (userJavaLangObjectNull) {
user.setName(((java.lang.String) (callbackArg_0)));
}
}
};
public ActivityMainBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
this(bindingComponent, root, mapBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds));
}
private ActivityMainBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
super(bindingComponent, root, 1
, (android.widget.EditText) bindings[2]
, (android.widget.TextView) bindings[1]
);
this.mboundView0 = (androidx.constraintlayout.widget.ConstraintLayout) bindings[0];
this.mboundView0.setTag(null);
this.name.setTag(null);
this.text.setTag(null);
setRootTag(root);
// listeners
invalidateAll();
}
@Override
public void invalidateAll() {
synchronized (this) {
mDirtyFlags = 0x8L;
}
requestRebind();
}
@Override
public boolean hasPendingBindings() {
synchronized (this) {
if (mDirtyFlags != 0) {
return true;
}
}
return false;
}
@Override
public boolean setVariable(int variableId, @Nullable Object variable) {
boolean variableSet = true;
if (BR.user == variableId) {
setUser((com.demo.study.model.User) variable);
} else {
variableSet = false;
}
return variableSet;
}
public void setUser(@Nullable com.demo.study.model.User User) {
updateRegistration(0, User);
this.mUser = User;
synchronized (this) {
mDirtyFlags |= 0x1L;
}
notifyPropertyChanged(BR.user);
super.requestRebind();
}
@Override
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
switch (localFieldId) {
case 0:
return onChangeUser((com.demo.study.model.User) object, fieldId);
}
return false;
}
private boolean onChangeUser(com.demo.study.model.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;
} else if (fieldId == BR.password) {
synchronized (this) {
mDirtyFlags |= 0x4L;
}
return true;
}
return false;
}
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized (this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
java.lang.String userName = null;
com.demo.study.model.User user = mUser;
java.lang.String userNameCharUserPassword = null;
java.lang.String userNameChar = null;
java.lang.String userPassword = null;
if ((dirtyFlags & 0xfL) != 0) {
if (user != null) {
// read user.name
userName = user.getName();
// read user.password
userPassword = user.getPassword();
}
// read (user.name) + ('-')
userNameChar = (userName) + ('-');
// read ((user.name) + ('-')) + (user.password)
userNameCharUserPassword = (userNameChar) + (userPassword);
}
// batch finished
if ((dirtyFlags & 0xbL) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.name, userName);
}
if ((dirtyFlags & 0x8L) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.name, (androidx.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged) null, (androidx.databinding.adapters.TextViewBindingAdapter.OnTextChanged) null, (androidx.databinding.adapters.TextViewBindingAdapter.AfterTextChanged) null, nameandroidTextAttrChanged);
}
if ((dirtyFlags & 0xfL) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.text, userNameCharUserPassword);
}
}
// Listener Stub Implementations
// callback impls
// dirty flag
private long mDirtyFlags = 0xffffffffffffffffL;
/* flag mapping
flag 0 (0x1L): user
flag 1 (0x2L): user.name
flag 2 (0x3L): user.password
flag 3 (0x4L): null
flag mapping end*/
//end
}
二、DataBinding Apt/Kapt后产物
2.1、DataBinderMapper
DataBindingUtil类在被加载时(在 Activity.onCreate 中调用),根据成员变量的生命周期,会先初始这个 mapper。
public class DataBindingUtil {
private static DataBinderMapper sMapper = new DataBinderMapperImpl();
private static DataBindingComponent sDefaultComponent = null;
}
该 mapper 也是在 apt/kapt 编译阶段生成的:
build/generated/source/kapt/<环境>/androidx/databinding/DataBinderMapperImpl.java
2.1.1、DataBinderMapperImpl
package androidx.databinding;
public class DataBinderMapperImpl extends MergedDataBinderMapper {
DataBinderMapperImpl() {
// 调用父类 MergedDataBinderMapper.addMapper 方法
addMapper(new com.demo.study.DataBinderMapperImpl());
}
}
2.1.2、MergedDataBinderMapper.addMapper
public class MergedDataBinderMapper extends DataBinderMapper {
private Set<Class<? extends DataBinderMapper>> mExistingMappers = new HashSet<>();
private List<DataBinderMapper> mMappers = new CopyOnWriteArrayList<>();
public void addMapper(DataBinderMapper mapper) {
Class<? extends DataBinderMapper> mapperClass = mapper.getClass();
// 如果不存在,先加入 mExistingMappers
if (mExistingMappers.add(mapperClass)) {
// 再加入 mMappers
mMappers.add(mapper);
// 获取该 mapper 所依赖的其它 DataBinderMapper
// mapper = com.demo.study.DataBinderMapperImpl
// 该 mapper 中实现了 collectDependencies 方法
final List<DataBinderMapper> dependencies = mapper.collectDependencies();
for(DataBinderMapper dependency : dependencies) {
addMapper(dependency);
}
}
}
}
2.1.3、DataBinderMapperImpl
该类同样也是自动生成:
build/generated/source/kapt/<环境>/<包名>/databinding/DataBinderMapperImpl.java
public class DataBinderMapperImpl extends DataBinderMapper {
@Override
public List<DataBinderMapper> collectDependencies() {
ArrayList<DataBinderMapper> result = new ArrayList<DataBinderMapper>(1);
result.add(new androidx.databinding.library.baseAdapters.DataBinderMapperImpl());
return result;
}
}
返回的类来自于『databinding-adapters』包。
2.2、res/layout编译后的产物
2.2.1、原始布局(activity_main.xml 为例)
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable name="user" type="com.demo.study.model.User" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name + '-' + user.password}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/name"
android:inputType="text"
android:importantForAutofill="no"
android:text="@={user.name}"
android:hint="输入手机号"
android:layout_width="200dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/text"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"/>
<EditText
android:id="@+id/pswd"
android:inputType="text"
android:importantForAutofill="no"
android:text="@={user.password}"
android:hint="输入密码"
android:layout_width="200dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/name"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
该 layout 中:
- data 属性标签,引入具体的 model 类,并指定其名称
- 有三个控件:
- 1个 TextView,单向绑定来显示 model 中的数据;
- 2个 EditText,分别双向绑定 model 中的 name 和 password 字段;
2.2.2、编译后的产物
编译后有会有两个 xml 文件
2.2.2.1、中间产物 activity_main-layout.xml
注意该中间产物的信息:
- Variables 是 import 的 Model,可以是多个;
- Targets 是 Model 绑定到 View 上的信息:
- 每个根视图,会添加 tag,规则为: layout/activity_xx_数字 (根据在 AndroidManifest.xml 中注册的顺序,从0开始);
- 但凡 view 与 model 有绑定,就会为其添加 tag,规则会:binding_数字(根据布局中从上到下的顺序,从1开始);
- 但凡有绑定的view控件,都会有 Expressions 表达式,其中最主要的就是 TwoWay 这个标签,告诉 DataBinding 是单向还是双向绑定;
2.2.2.2、运行时 activity_main.xml
该文件为我们实际运行时的真正的布局文件,去掉了最外层 layout 标签,以及 variables 标签,还原成和我们没有用 DataBinding 时的布局一样,只不过,添加了 tag 属性,去掉了显示数据的属性(如:文件控件 text 属性)。
2.3、Activity布局生成的ViewBinding的抽象类和实现类
每个 Activity 都会有一个对应的 layout.xml,APT 后,会根据 2.2.2 中的产物,生成继承于 ViewDataBinding 的类:
- 抽象类(ActivityXXXBinding extends ViewDataBinding):view & mode 的实例
build/generated/data_binding_base_class_source_out/<环境>/dataBindingGenBaseClassesDebug/out/<包名>/databinding/......
- 实现类(ActivityXXXBindingImpl extends ActivityXXXBinding):该Activity绑定监听 & 执行刷新
build/generated/source/kapt/<环境>/<包名>/databinding/......
三、DataBinding 启动 & 异步渲染流程
Activity.onCreate 调用 DataBindingUtil.setContentView 直到完成,做了哪些事?
正式分析之前,先上一张真机的『Layout Inspector』(在 Tools 菜单中)图
DecorView包含的子孙Views(DecorView的父View是PhoneWindow,它是 Window 的唯一子类,再往上就是 Activity 了):
- LinearLayout
- R.id.decor_content_parent
- R.id.content (真正的内容ViewGroup)
- R.id.action_bar_container (标题栏)
- R.id.statusBarBackground (状态栏)
3.1、DataBindingUtil.setContentView
public class DataBindingUtil {
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.setContentView(layoutId);
// Activity -> PhoneWindow -> DecorView
View decorView = activity.getWindow().getDecorView();
// 查找 R.id.content ViewGroup
ViewGroup contentView = (ViewGroup) decorView.findViewById(android.R.id.content);
return bindToAddedViews(bindingComponent, contentView, 0, layoutId);
}
}
3.2、DataBindingUtil.bindToAddedViews
public class DataBindingUtil {
private static <T extends ViewDataBinding> T bindToAddedViews(
DataBindingComponent component,
ViewGroup parent, // contentView,肯定只有一个 child 且是 ViewGroup
int startChildren, int layoutId // layoutId = R.layout.xxxx,我们项目中的 layout xml
) {
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);
}
}
}
3.3、DataBindingUtil.bind
public class DataBindingUtil {
static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View root, int layoutId) {
// 通过 DataBinderMapper 来查找具体的 XxxViewBinding
// sMapper 中有两个 mapper:
// [0]: 是 DataBinding APT 为我们生成的 DataBinderMapperImpl
// [1]: 是 DataBinding Lib 中 baseAdapters 下的 DataBinderMapperImpl
return (T) sMapper.getDataBinder(bindingComponent, root, layoutId);
}
}
3.4、MergedDataBinderMapper.getDataBinder
public class MergedDataBinderMapper extends DataBinderMapper {
@Override
public ViewDataBinding getDataBinder(DataBindingComponent bindingComponent, View view,int layoutId) {
// 遍历 mapper
for(DataBinderMapper mapper : mMappers) {
ViewDataBinding result = mapper.getDataBinder(bindingComponent, view, layoutId);
if (result != null) {
return result;
}
}
if (loadFeatures()) {
return getDataBinder(bindingComponent, view, layoutId);
}
return null;
}
}
3.5、<包名>.DataBinderMapperImpl.getDataBinder
// 自动生成的
package com.demo.study;
public class DataBinderMapperImpl extends DataBinderMapper {
private static final int LAYOUT_ACTIVITYMAIN = 1;
private static final int LAYOUT_ACTIVITYSECOND = 2;
private static final SparseIntArray INTERNAL_LAYOUT_ID_LOOKUP = new SparseIntArray(2);
static {
INTERNAL_LAYOUT_ID_LOOKUP.put(com.demo.study.R.layout.activity_main, LAYOUT_ACTIVITYMAIN);
INTERNAL_LAYOUT_ID_LOOKUP.put(com.demo.study.R.layout.activity_second, LAYOUT_ACTIVITYSECOND);
}
@Override
public ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) {
// 根据 layoutId 来查找 LAYOUT_ACTIVITYXXXXX
int localizedLayoutId = INTERNAL_LAYOUT_ID_LOOKUP.get(layoutId);
if(localizedLayoutId > 0) {
// 获取根视图的 tag: layout/activity_xxx_数字(如:layout/activity_main_0)
final Object tag = view.getTag();
if(tag == null) {
throw new RuntimeException("view must have a tag");
}
// 根据 tag 找到对应的 ViewBinding 实现类
switch(localizedLayoutId) {
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);
}
case LAYOUT_ACTIVITYSECOND: {
if ("layout/activity_second_0".equals(tag)) {
return new ActivitySecondBindingImpl(component, view);
}
throw new IllegalArgumentException("The tag for activity_second is invalid. Received: " + tag);
}
}
}
return null;
}
}
3.6、AcvitityXXXBindingImpl(公有构造函数)
public class ActivityMainBindingImpl extends ActivityMainBinding {
public ActivityMainBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
// 重点看 mapBindings 方法
// root = 根视图( tag = layout/activity_main_0 )
// 为啥是写死是 4 ?这个就是中间产物 activity_main-layout.xml 中的 Targets 个数
this(bindingComponent, root, mapBindings(bindingComponent, root, 4, sIncludes, sViewsWithIds));
}
}
3.7、ViewDataBinding.mapBindings
public abstract class ViewDataBinding extends BaseObservable {
protected static Object[] mapBindings(DataBindingComponent bindingComponent, View root, int numBindings,
ViewDataBinding.IncludedLayouts includes, SparseIntArray viewsWithIds) {
// 创建 numBindings 个数的绑定对象数组
Object[] bindings = new Object[numBindings];
// 从我们的根视图开始,递归遍历找出所有含有 binding_数字 的控件,赋值到 bindings 中
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 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 开头
// 根布局的 tag = layout/activity_xxx_数字
......
} else if (tag != null && tag.startsWith(BINDING_TAG_PREFIX)) {
// 判断 tag 是否以 binding_ 开头
// 绑定的控件的 tag = binding_数字
......
} else {
// Not a bound view
indexInIncludes = -1;
}
......
if (view instanceof ViewGroup) {
final ViewGroup viewGroup = (ViewGroup) view;
final int count = viewGroup.getChildCount();
for (int i = 0; i < count; i++) {
// 遍历 ViewGroup 的 children
.......
if (!isInclude) {
// 递归遍历子孙
mapBindings(bindingComponent, child, bindings, includes, viewsWithIds, false);
}
}
}
}
}
3.8、AcvitityXXXBindingImpl(私有构造函数)
public class ActivityMainBindingImpl extends ActivityMainBinding {
private ActivityMainBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
super(bindingComponent, root, 1
, (android.widget.EditText) bindings[2]
, (android.widget.EditText) bindings[3]
, (android.widget.TextView) bindings[1]
);
// 将4个binding控件 tag 置 null
this.mboundView0 = (androidx.constraintlayout.widget.ConstraintLayout) bindings[0];
this.mboundView0.setTag(null);
this.name.setTag(null);
this.pswd.setTag(null);
this.text.setTag(null);
// 设置根布局
setRootTag(root);
// listeners
invalidateAll();
}
@Override
public void invalidateAll() {
synchronized(this) {
mDirtyFlags = 0x8L;
}
requestRebind();
}
}
3.9、ViewDataBinding.requestRebind
public abstract class ViewDataBinding extends BaseObservable {
protected void requestRebind() {
if (mContainingBinding != null) {
mContainingBinding.requestRebind();
} else {
// LifecycleOwner
......
synchronized (this) {
if (mPendingRebind) {
return;
}
mPendingRebind = true;
}
// 重点哈:详见 3.10
// 如果设备系统 SDK 版本高于 API-16,即 SDK 4.1+,就用 mChoreographer 来渲染
if (USE_CHOREOGRAPHER) {
// mChoreographer 可以用来监控 fps
mChoreographer.postFrameCallback(mFrameCallback);
} else {
// 使用 UI主线程的 Handler 来运行 Runnable
mUIThreadHandler.post(mRebindRunnable);
}
// 最终都会执行 mRebindRunnable
}
}
}
3.10、ViewDataBinding(构造函数)
在构造函数中,会创建:
- Choreographer.FrameCallback
或 - 绑定至主线程的 Handler
public abstract class ViewDataBinding extends BaseObservable {
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) {
mChoreographer = Choreographer.getInstance();
mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
mRebindRunnable.run();
}
};
} else {
mFrameCallback = null;
mUIThreadHandler = new Handler(Looper.myLooper());
}
}
}
3.11、mRebindRunnable
public abstract class ViewDataBinding extends BaseObservable {
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;
}
}
// 最终调用 AcvitityXXXBindingImpl.executePendingBindings
executePendingBindings();
}
};
}
3.12、AcvitityXXXBindingImpl.executePendingBindings
public class ActivityMainBindingImpl extends ActivityMainBinding {
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
com.demo.study.model.User user = mUser;
java.lang.String userNameCharUserPassword = null;
java.lang.String userPassword = null;
java.lang.String userName = null;
java.lang.String userNameChar = null;
if ((dirtyFlags & 0xfL) != 0) {
if (user != null) {
// read user.password
userPassword = user.getPassword();
// read user.name
userName = user.getName();
}
userNameCharUserPassword = (userNameChar) + (userPassword);
}
// 根据不同标志位(编译时动态决定),刷新 UI 控件
if ((dirtyFlags & 0xbL) != 0) {
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.name, userName);
}
if ((dirtyFlags & 0x8L) != 0) {
androidx.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.name, (androidx.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.OnTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.AfterTextChanged)null, nameandroidTextAttrChanged);
androidx.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.pswd, (androidx.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.OnTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.AfterTextChanged)null, pswdandroidTextAttrChanged);
}
if ((dirtyFlags & 0xdL) != 0) {
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.pswd, userPassword);
}
if ((dirtyFlags & 0xfL) != 0) {
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.text, userNameCharUserPassword);
}
}
}
至此,我们的 Activity 从启动、获取 Binding 对象、异步渲染就分析完成!