美文网首页
Databinding原理解析(二)-View绑定数据

Databinding原理解析(二)-View绑定数据

作者: 烧伤的火柴 | 来源:发表于2020-04-30 14:54 被阅读0次

    介绍

    上一篇介绍了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);设置值了。

    相关文章

      网友评论

          本文标题:Databinding原理解析(二)-View绑定数据

          本文链接:https://www.haomeiwen.com/subject/svlzwhtx.html