【DataBinding】更新视图的逻辑:从一个bug说起

我在项目中使用了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()

猜你喜欢

转载自blog.csdn.net/qq_23049111/article/details/125912106
今日推荐