介绍
上一篇介绍了Databinding实例的创建过程,ActivityMainBindingImpl的构造方法中得到了布局文件中的所有View放到了一个数组bindings中。这一篇讲述View绑定数据的过程。
binding.png分析
private ActivityMainBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
super(bindingComponent, root, 2
, (android.widget.Button) bindings[3]
);//1
//给view赋值
this.getNickBtn.setTag(null);
this.mboundView0 = (android.widget.LinearLayout) bindings[0];
this.mboundView0.setTag(null);
this.mboundView1 = (android.widget.TextView) bindings[1];
this.mboundView1.setTag(null);
this.mboundView2 = (android.widget.EditText) bindings[2];
this.mboundView2.setTag(null);
setRootTag(root);
// listeners
invalidateAll();//3
}
protected ActivityMainBinding(Object _bindingComponent, View _root, int _localFieldCount,
Button getNickBtn) {
super(_bindingComponent, _root, _localFieldCount);
this.getNickBtn = getNickBtn;
}
注释1处调用父类的构造,因为我们在activity_main.xml中只有一个button定义了id,所以在编译的时候getNickBtn字段在父类(ActivityMainBinding)中定义了。
@NonNull
public final Button getNickBtn;
两个View没有定义id,所以是本地变量在ActivityMainBindingImpl定义, 所以_localFieldCount是2.
我们看看注释3的实现
@Override
public void invalidateAll() {
synchronized (this) {
mDirtyFlags = 0x10L;
}
requestRebind();
}
因为外部更新user(model)的时候可能是在子线程中,所以对mDirtyFlags加锁保证线程安全。mDirtyFlags的名字很奇怪叫做 肮脏的标记,但是这里的作用是更新ui的标记。
我们看一下请求Rebind的内容
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)) {//1
return; // wait until lifecycle owner is started
}
}
synchronized (this) {
if (mPendingRebind) {
return;
}
mPendingRebind = true;
}
if (USE_CHOREOGRAPHER) {
mChoreographer.postFrameCallback(mFrameCallback);//2
} else {
mUIThreadHandler.post(mRebindRunnable);
}
}
}
本例子的布局没有嵌套所以mContainingBinding是null,通过注释1处我们可以知道数据绑定的动作必须要至少在activity在前台可见的时候进行,但是当前的owner是null。无论调用层传递model是在什么线程,这里都要保证数据的绑定动作都要在主线程中,所以使用Handler处理,由于当前的sdk>16所以调用注释2处,系统会在渲染下一帧的时候回调mFrameCallback,mFrameCallback是在我们new ActivityMainBindingImpl中调用父类构造函数的时候初始化的
protected ViewDataBinding(DataBindingComponent bindingComponent, View root, int localFieldCount) {
...
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());
}
}
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()) {//1
// Don't execute the pending bindings until the View
// is attached again.
mRoot.removeOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
mRoot.addOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
return;
}
}
executePendingBindings();
}
};
执行bindings的动作必须是在mRoot挂载到window上才可以。
public void executePendingBindings() {
if (mContainingBinding == null) {
executeBindingsInternal();
} else {
mContainingBinding.executePendingBindings();
}
}
mContainingBinding 是null,所以指定 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;
}
为了保证绑定工作的线性完成,前边加了很多判断,我们的构造函数中没有实例化mRebindCallbacks ,所以执行executeBindings(),executeBindings是一个抽象方法,在ActivityMainBindingImpl中实现。真正的绑定动作是在具体的XXBindingImpl中实现的。
protected void executeBindings() {
long dirtyFlags = 0;
synchronized (this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
java.lang.String userInfoNameGet = null;
android.view.View.OnClickListener listener = mListener;
androidx.databinding.ObservableField<java.lang.String> userInfoNickName = null;
java.lang.String userInfoNickNameGet = null;
androidx.databinding.ObservableField<java.lang.String> userInfoName = null;
com.jawe.study.databinding2.UserInfo userInfo = mUserInfo;
if ((dirtyFlags & 0x14L) != 0) {
}
if ((dirtyFlags & 0x1bL) != 0) {
if ((dirtyFlags & 0x19L) != 0) {
if (userInfo != null) {
// read userInfo.nickName
userInfoNickName = userInfo.getNickName();
}
updateRegistration(0, userInfoNickName);
if (userInfoNickName != null) {
// read userInfo.nickName.get()
userInfoNickNameGet = userInfoNickName.get();
}
}
if ((dirtyFlags & 0x1aL) != 0) {
if (userInfo != null) {
// read userInfo.name
userInfoName = userInfo.getName();
}
updateRegistration(1, userInfoName);
if (userInfoName != null) {
// read userInfo.name.get()
userInfoNameGet = userInfoName.get();
}
}
}
// batch finished
if ((dirtyFlags & 0x14L) != 0) {
// api target 1
this.getNickBtn.setOnClickListener(listener);
}
if ((dirtyFlags & 0x1aL) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView1, userInfoNameGet);
}
if ((dirtyFlags & 0x19L) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView2, userInfoNickNameGet);
}
if ((dirtyFlags & 0x10L) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.mboundView2, (androidx.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged) null, (androidx.databinding.adapters.TextViewBindingAdapter.OnTextChanged) null, (androidx.databinding.adapters.TextViewBindingAdapter.AfterTextChanged) null, mboundView2androidTextAttrChanged);
}
}
在这里可以看到给View绑定值的实现,所有的实现都先判断不是null,所以Databinding的一个优点就是不会出现null异常。
注意
但是我们在创建ActivityMainBinding实例的时候,并没有设置userInfo,所以到目前位置视图还是空白的。
设置model
我们看一下setUserInfo发生的事情。
public void setUserInfo(@Nullable com.jawe.study.databinding2.UserInfo UserInfo) {
this.mUserInfo = UserInfo;
synchronized(this) {
mDirtyFlags |= 0x8L;
}
notifyPropertyChanged(BR.userInfo);
super.requestRebind();
}
我们看一下notifyPropertyChanged的实现
public void notifyPropertyChanged(int fieldId) {
synchronized (this) {
if (mCallbacks == null) {
return;
}
}
mCallbacks.notifyCallbacks(this, fieldId, null);
}
这个例子中我们是使用的ObservableField实现数据观察的,userInfo本身是没有添加mCallbacks的,所以这里的mCallbacks是null。
回到setUserInfo方法内,又去执行requestRebind,接着执行前边分析的流程,这一次到executeBindings的时候,userInfo和里边的字段都有值了,textview会通过如下方式设置值。
@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;
}
if (text instanceof Spanned) {
if (text.equals(oldText)) {
return; // No change in the spans, so don't set anything.
}
} else if (!haveContentsChanged(text, oldText)) {
return; // No content changes, so don't set anything.
}
view.setText(text);
}
text不是null,通过view.setText(text);
设置值了。
网友评论