概述
我在项目中使用了DataBinding,但是数据并没有更新。
项目代码
为了查看方便,精简了代码:
在xml中导入了Entity类,并使用Entity的数据赋值
<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="entity"
type="Entity"
/>
</data>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
//省略
</RelativeLayout>
</layout>
在代码中设置了entity:
val inflate = DataBindingUtil.inflate<LayoutBinding>(
context.layoutInflater,
layout,
null,
false
)
inflate.entity = entity
运行后发现界面并没有更新数据,和DataBinding的文档反复对照了很多次,依然没有解决,不得已开始撸源码,入口是:
inflate.entity = entity
源码
DataBinding生成的代码:
public void setEntity(@Nullable Entity Entity) {
this.mEntity = Entity;
synchronized(this) {
mDirtyFlags |= 0x1L;
}
notifyPropertyChanged(BR.entity);
super.requestRebind();
}
我们关注super.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);
}
}
}
这里我们看到了两个回调,先看mFrameCallback:
mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
mRebindRunnable.run();
}
};
mFrameCallback 内部也是调用的mRebindRunnable:
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();
}
};
在mRebindRunnable我们发现这里有一个判断:当sdk版本大于19时,如果mRoot(根View)没有添加到windows,那么就给mRoot添加一个回调,并且return,不会执行executePendingBindings,从方法名可以看出来,executePendingBindings就是数据绑定的方法。
那么我们继续看看ROOT_REATTACHED_LISTENER这个回调是怎么定义的:
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) {
// execute the pending bindings.
final ViewDataBinding binding = getBinding(v);
binding.mRebindRunnable.run();
v.removeOnAttachStateChangeListener(this);
}
@Override
public void onViewDetachedFromWindow(View v) {
}
};
}
}
这个监听方法也是调用mRebindRunnable。山路十八弯,最终还是绕回了mRebindRunnable。
处理bug
看完源码,我马上反应了过来bug的位置,项目的View生成Bitmap就丢弃了,并没有添加到任何一个Window上,绑定数据的方法不会被执行。
从源码中发现,mRebindRunnable最后执行executePendingBindings绑定数据,而这个方法是公开的。
public void executePendingBindings() {
if (mContainingBinding == null) {
executeBindingsInternal();
} else {
mContainingBinding.executePendingBindings();
}
}
所以,只要手动调用一下,这个bug就解决了:
val inflate = DataBindingUtil.inflate<LayoutBinding>(
context.layoutInflater,
layout,
null,
false
)
inflate.entity = entity
//手动调用绑定数据
inflate.executePendingBindings()
最后
以上就是花痴玉米为你收集整理的【DataBinding】更新视图的逻辑:从一个bug说起项目代码源码处理bug的全部内容,希望文章能够帮你解决【DataBinding】更新视图的逻辑:从一个bug说起项目代码源码处理bug所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复