介绍
传统的应用开发中,布局文件通常只负责应用界面的布局工作,如果需要实现页面交互就需要调用setContentView()将Activity、fragment和XML布局文件关联起来。然后通过控件的id找到控件,接着在页面中通过代码对控件进行逻辑处理。页面承担了大部分的工作量,大量的逻辑处理需要在Activity、Fragment中进行处理,因此页面显得臃肿不堪,维护起来也很困难,为了减轻页面的工作量,Google提出了DataBiiding(视图绑定)
DataBinding的主要特点
- 代码更加简洁,可读性高,能够在XML文件中实现UI控制。
- 不再需要findViewById操作,
- 能够与Model实体类直接绑定,易于维护和扩展。
基本使用
开启viewBinding
视图绑定功能可按模块启用,要在某个模块中启用视图绑定,请将 viewBinding 元素添加到build.gradle 文件中
android {
...
viewBinding {
enabled = true
}
}
开启之后在依赖中会多四个aar
修改布局文件
按住【Alt + 回车键】然后选择 【Convert to data binding layout】也可以生成 DataBinding 需要的布局
<?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>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
重新编译项目DataBinding库就会生成对应的Binding类,该类用来实现XML布局文件与Model类的绑定
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;
public ActivityMainBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
this(bindingComponent, root, mapBindings(bindingComponent, root, 1, sIncludes, sViewsWithIds));
}
private ActivityMainBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
super(bindingComponent, root, 0
);
this.mboundView0 = (androidx.constraintlayout.widget.ConstraintLayout) bindings[0];
this.mboundView0.setTag(null);
setRootTag(root);
// listeners
invalidateAll();
}
@Override
public void invalidateAll() {
synchronized(this) {
mDirtyFlags = 0x1L;
}
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;
return variableSet;
}
@Override
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
switch (localFieldId) {
}
return false;
}
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
// batch finished
}
private long mDirtyFlags = 0xffffffffffffffffL;
}
生成Binding类的名字很特殊,它与XML布局文件的名字有对应关系,具体的联系就是,以XML布局文件为准,去掉下划线,所有单次以大驼峰的形式按顺序拼接,最后再加上Binding。
绑定布局
var binding: ActivityMainBinding ? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding=DataBindingUtil.setContentView(this,R.layout.activity_main)
}
使用DataBindingUtil类的setContentView()方法对Activity进行绑定,其返回值就是工具生成的Binding类
添加data标签
<data>
<variable
name="user"
type="com.arrom.demo.User" />
</data>
给控件赋值
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
在XML文件中声明好variable属性后,接下来就可以在XML使用它了。使用variable属性时需要使用到布局表达式: @{ }。可以在布局表达式@{ }中获取传入variable对象的值
binding=DataBindingUtil.setContentView(this,R.layout.activity_main)
val bean = User("arrom", 18);
binding?.user = bean;
在UI界面中使用Binding类为每一个variable标签使用set方法传递数据
响应事件
我们还可以使用DataBinding响应用户事件
第一种方式
binding?.clickBtn?.setOnClickListener {
Log.d("MainActivity","按钮点击事件")
}
第二种方式
首先创建一个工具类,在类中定义响应事件的方法
class ButtonClickListener {
fun clickBtn(view: View){
Log.d("ButtonClickListener","点击事件")
}
}
然后在布局文件中添加点击事件的代码
<data>
<variable
name="user"
type="com.arrom.demo.User" />
<variable
name="clickBtn"
type="com.arrom.demo.ButtonClickListener" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/clickBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="点击按钮"
android:onClick="@{clickBtn.clickBtn}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.73" />
</androidx.constraintlayout.widget.ConstraintLayout>
首先在data标签中为ButtonClickListener类声明对象,在Button的onClick属性中传入布局表达式即可。
include标签
为了能够让布局文件得到复用,在编写布局的时候我们经常会使用include标签,相同结构与内容的布局文件就可以在多处使用。但是如果一个布局文件中使用了DataBinding,同时也使用了include标签,那么如何使用include标签引入的布局文件中中的数据呢?
我们需要在同一级页面的include标签中,通过命名控件xmlns:app来引入布局变量User,将数据对象传递给二级页面
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
>
<data>
<variable
name="user"
type="com.arrom.demo.User" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/use_item"
app:persondata="@{user}"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
布局表达式中直接传入页面变量user,include标签属性值可以任意取名
use_item.xml
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="com.arrom.demo.User" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:layout_marginBottom="30dp"
android:lineSpacingExtra="20sp"
android:text="@{user.name}"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:layout_editor_absoluteX="157dp"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
小结
在编译时,dataBinding会内部处理布局文件,重新生成两个布局文件。一个与布局文件同名,一个叫activity_main-layout.xml
生成后的activity_main.xml就跟我们普通的xml文件一样,用于Android os的渲染。但是DataBinding会在每个控件上都加上Tag,这个Tag就是用来快速查找和定位具体控件的。
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" android:tag="layout/activity_main_0" 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">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:tag="binding_1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/clickBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="点击按钮"
android:tag="binding_2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.73" />
</androidx.constraintlayout.widget.ConstraintLayout>
activity_main.xml与不用DataBinding前的布局文件差不多,唯一的区别是加了tag标记。 根布局的tag为layout/activity_main_0
activity_main-layout.xml文件,是DataBinding生成的配置文件,它能够通过文件中的配置信息,快速定位哪一个控件,该显示什么信息。
<Layout layout="activity_main" modulePackage="com.arrom.demo" filePath="app/src/main/res/layout/activity_main.xml" directory="layout" isMerge="false" isBindingData="true" rootNodeType="androidx.constraintlayout.widget.ConstraintLayout">
<Variables declared="true" type="com.arrom.demo.User" name="user">
<location startLine="6" startOffset="7" endLine="8" endOffset="39"/>
</Variables>
<Variables declared="true" type="com.arrom.demo.ButtonClickListener" name="clickBtn">
<location startLine="9" startOffset="8" endLine="11" endOffset="55"/>
</Variables>
<Targets>
<Target tag="layout/activity_main_0" view="androidx.constraintlayout.widget.ConstraintLayout">
<Expressions/>
<location startLine="14" startOffset="4" endLine="41" endOffset="55"/>
</Target>
<Target tag="binding_1" view="TextView">
<Expressions>
<Expression text="user.name" attribute="android:text">
<Location startLine="22" startOffset="12" endLine="22" endOffset="38"/>
<TwoWay>false</TwoWay>
<ValueLocation startLine="22" startOffset="28" endLine="22" endOffset="36"/>
</Expression>
</Expressions>
<location startLine="19" startOffset="8" endLine="26" endOffset="55"/>
</Target>
<Target id="@+id/clickBtn" tag="binding_2" view="TextView">
<Expressions>
<Expression text="clickBtn.clickBtn" attribute="android:onClick">
<Location startLine="33" startOffset="12" endLine="33" endOffset="49"/>
<TwoWay>false</TwoWay>
<ValueLocation startLine="33" startOffset="31" endLine="33" endOffset="47"/>
</Expression>
</Expressions>
<location startLine="28" startOffset="8" endLine="39" endOffset="55"/>
</Target>
</Targets>
</Layout>
源码分析
setContentView方法
DataBindingUtil.setContentView(this,R.layout.activity_main)
进入DataBindingUtil类中setContentView 方法
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);
View decorView = activity.getWindow().getDecorView();
ViewGroup contentView = (ViewGroup) decorView.findViewById(android.R.id.content);
return bindToAddedViews(bindingComponent, contentView, 0, layoutId);
}
进入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);
}
}
childrenAdded = 1 所以进入bind方法
static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View[] roots,
int layoutId) {
return (T) sMapper.getDataBinder(bindingComponent, roots, layoutId);
}
sMapper 是DataBinderMapperImpl
private static DataBinderMapper sMapper = new DataBinderMapperImpl();
进入DataBinderMapperImpl 的getDataBinder 方法
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_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;
}
根布局为layout/activity_main_0这个tag所以进入ActivityMainBindingImpl 类中
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, 0
, (android.widget.TextView) bindings[2]
);
this.clickBtn.setTag(null);
this.mboundView0 = (androidx.constraintlayout.widget.ConstraintLayout) bindings[0];
this.mboundView0.setTag(null);
this.mboundView1 = (android.widget.TextView) bindings[1];
this.mboundView1.setTag(null);
setRootTag(root);
// listeners
invalidateAll();
}
先执行了父类的构造方法,这个父类最后定位到ViewDataBinding这个类
static {
if (VERSION.SDK_INT < VERSION_CODES.KITKAT) {
ROOT_REATTACHED_LISTENER = null;
} else {
ROOT_REATTACHED_LISTENER = new OnAttachStateChangeListener() {
@TargetApi(VERSION_CODES.KITKAT)
@Override
public void onViewAttachedToWindow(View v) {
final ViewDataBinding binding = getBinding(v);
binding.mRebindRunnable.run();
v.removeOnAttachStateChangeListener(this);
}
@Override
public void onViewDetachedFromWindow(View v) {
}
};
}
}
静态代码块中主要是一个监听,简单来说就是,当View和屏幕绑定时,这个监听就会触发,这里指的是mRootView
/**
* Runnable executed on animation heartbeat to rebind the dirty Views.
*/
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();
}
};
当静态代码块中的监听执行时,这个类的run方法就执行了
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());
}
}
这里使用了Choreographer这个类
回到ActivityMainBindingImpl的构造方法中,查看invalidateAll方法
public void invalidateAll() {
synchronized(this) {
mDirtyFlags = 0x4L;
}
requestRebind();
}
requestRebind方法是父类ViewDataBinding的方法
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 {
mUIThreadHandler.post(mRebindRunnable);
}
}
}
我们可以看到上面说的mChoreographer,它执行了postFrameCallback方法,当doFrame方法执行时,mRebindRunnable的run方法就执行了。mRebindRunnable中的run方法执行了两个内容:如果mRoot已经和屏幕进行了绑定,那么直接执行executePendingBindings方法;否则的话,添加监听,什么时候绑定了,什么时候执行
进入executePendingBindings方法
public void executePendingBindings() {
if (mContainingBinding == null) {
executeBindingsInternal();
} else {
mContainingBinding.executePendingBindings();
}
}
进入executeBindingsInternal 方法中
private void executeBindingsInternal() {
if (mIsExecutingPendingBindings) {
requestRebind();
return;
}
if (!hasPendingBindings()) {
return;
}
mIsExecutingPendingBindings = true;
mRebindHalted = false;
if (mRebindCallbacks != null) {
mRebindCallbacks.notifyCallbacks(this, REBIND, null);
// The onRebindListeners will change mPendingHalted
if (mRebindHalted) {
mRebindCallbacks.notifyCallbacks(this, HALTED, null);
}
}
if (!mRebindHalted) {
executeBindings();
if (mRebindCallbacks != null) {
mRebindCallbacks.notifyCallbacks(this, REBOUND, null);
}
}
mIsExecutingPendingBindings = false;
}
进入executeBindings方法中(这个方法是一个抽象方法,具体的实现在ActivityMainBindingImpl中)
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
java.lang.String userName = null;
com.arrom.demo.User user = mUser;
com.arrom.demo.ButtonClickListener ClickBtn1 = mClickBtn;
android.view.View.OnClickListener clickBtnClickBtnAndroidViewViewOnClickListener = null;
if ((dirtyFlags & 0x5L) != 0) {
if (user != null) {
// read user.name
userName = user.getName();
}
}
if ((dirtyFlags & 0x6L) != 0) {
if (ClickBtn1 != null) {
// read clickBtn::clickBtn
clickBtnClickBtnAndroidViewViewOnClickListener = (((mClickBtnClickBtnAndroidViewViewOnClickListener == null) ? (mClickBtnClickBtnAndroidViewViewOnClickListener = new OnClickListenerImpl()) : mClickBtnClickBtnAndroidViewViewOnClickListener).setValue(ClickBtn1));
}
}
// batch finished
if ((dirtyFlags & 0x6L) != 0) {
// api target 1
this.clickBtn.setOnClickListener(clickBtnClickBtnAndroidViewViewOnClickListener);
}
if ((dirtyFlags & 0x5L) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView1, userName);
}
}
在这里把控件和model进行了绑定,不过此时的mUser还是空。
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView1, userName);
把从JavaBean属性中读出来的值,设置到View上。也就是实现了Model到View更新。
大致流程图
setUser方法分析
public void setUser(@Nullable com.arrom.demo.User User) {
//更新注册
updateRegistration(0, User);
this.mUser = User;
synchronized(this) {
mDirtyFlags |= 0x1L;
}
notifyPropertyChanged(BR.user);
super.requestRebind();
}
进入ViewDataBinding类中的updateRegistration方法
protected boolean updateRegistration(int localFieldId, Observable observable) {
return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
}
CREATE_PROPERTY_LISTENER创建属性的监听器
private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
@Override
public WeakListener create(
ViewDataBinding viewDataBinding,
int localFieldId,
ReferenceQueue<ViewDataBinding> referenceQueue
) {
// 返回一个属性监听器
return new WeakPropertyListener(viewDataBinding, localFieldId, referenceQueue)
.getListener();
}
};
进入updateRegistration方法
protected boolean updateRegistration(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
if (observable == null) {
return unregisterFrom(localFieldId);
}
//将WeakListener 变量保存在mLocalFieldObservers 因为WeakListener是弱引用,所以需要判空,如果是空的就注册
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;
}
localFieldId 就是BR文件中的id observable就是观察者对象
进入registerTo方法
protected void registerTo(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
if (observable == null) {
return;
}
WeakListener listener = mLocalFieldObservers[localFieldId];
if (listener == null) {
//调用CreateWeakListener接口 的create,不能的databinding变量,实现了不同的CreateWeakListener接口
listener = listenerCreator.create(this, localFieldId, sReferenceQueue);
//这里把新创建的WeakListener 保存在数组,方便下次使用
mLocalFieldObservers[localFieldId] = listener;
if (mLifecycleOwner != null) {
listener.setLifecycleOwner(mLifecycleOwner);
}
}
//listener 是WeakListener 类型,
// observable 是业务类型的数据,也就是一种databinding变量
listener.setTarget(observable);
}
public void setTarget(T object) {
unregister();
mTarget = object;
if (mTarget != null) {
// mObservable 就是WeakPropertyListener
mObservable.addListener(mTarget);
}
}
通过WeakListener监听器中的ObservableReference对象保存观察者与被观察者,当被观察者发生改变的时候,就会找到对应的WeakListener监听器,然后通知观察者做修改
ObservableReference 对应对应的实现类WeakPropertyListener
private static class WeakPropertyListener extends Observable.OnPropertyChangedCallback
implements ObservableReference<Observable> {
final WeakListener<Observable> mListener;
public WeakPropertyListener(ViewDataBinding binder, int localFieldId) {
mListener = new WeakListener<Observable>(binder, localFieldId, this);
}
@Override
public WeakListener<Observable> getListener() {
return mListener;
}
@Override
public void addListener(Observable target) {
target.addOnPropertyChangedCallback(this);
}
@Override
public void removeListener(Observable target) {
target.removeOnPropertyChangedCallback(this);
}
//数据更新后,回调到这里去更新UI
@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);
}
}
target.addOnPropertyChangedCallback(this);调用了BaseObservable 的addOnPropertyChangedCallback函数
@Override
public void addOnPropertyChangedCallback(@NonNull OnPropertyChangedCallback callback) {
synchronized (this) {
if (mCallbacks == null) {
mCallbacks = new PropertyChangeRegistry();
}
}
// 相当于把一个观察者 callback,与被观察者BaseObservable ,建立联系,当BaseObservable 的数据发生变化,通知到callback,实现相应的UI更新
mCallbacks.add(callback);
}
进入requestRebind 方法
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 {
mUIThreadHandler.post(mRebindRunnable);
}
}
}
发现和setContentView里面的差不多,最后执行到了mRebindRunnable中的run方法,最后定位到executeBindings方法。
大致流程
其他知识点
- @Bindable注解 主要是生成BR中的索引
- BR文件存储了绑VM的id,功能与R文件类似。
- DataBinderMapper主要提供了从布局文件layoutId到ViewBinding类的映射。
- BaseObservable 主要做的就是把OnPropertyChangedCallback添加到PropertyChangeRegistry类型的mCallbacks中
- PropertyChangeRegistry用于管理Observable回调的实用程序类,继承自CallbackRegistry