美文网首页
Jetpack入门(三)viewModel介绍及dataBind

Jetpack入门(三)viewModel介绍及dataBind

作者: remax1 | 来源:发表于2020-07-20 11:11 被阅读0次

    前言

    mViewModel.getPatientList().observe(this, new Observer<String>() {
                @Override
                public void onChanged(String temp) {
                      
                }
            });
    

    在使用LifeData时,少不了observe(),在JetPack入门(二)这篇文章介绍过,在数据发生改变,如果需要更新UI,还需给数据增加一个监听,显然不够优雅,这当然不是我们希望看到的,我们希望的是数据发生改变之后UI能够自动更新,这就要用到dataBinding了,本文先从dataBinding原理开始介绍,再引出viewModel原理。

    XML解析原理

    解析入口:DataBindingUtil.setContentView(this,R.layout.activity_main);
    首先解析XML时会把分离成两个XML文件,目录在app/build/imtermediates/data_binding_layout_info_type_mege/对应的布局文件名称,app/build/imtermediates/incremental/mergeDebugResources/stripped.dir/layout/对应的布局文件名称。

    前者生成的格式:

    //目标 view 的id 与tag
    <Target id="@+id/tv1" tag="binding_1" view="TextView">
          <Expressions>
            //user.name就是@{}大括号里的内容
            <Expression text="user.name" attribute="android:text">
              //位置
              <Location startLine="20" startOffset="12" endLine="20" endOffset="38"/>
              <TwoWay>false</TwoWay>
              //位置
              <ValueLocation startLine="20" startOffset="28" endLine="20" endOffset="36"/>
            </Expression>
    

    后者生成的格式和常见的格式差不多,会引用到前者的tag值。

    <TextView
                android:id="@+id/tv2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:tag="binding_2"        
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent" />
    

    核心原理

    核心原理从setVariable(id,value)开始分析。
    id则是BR文件的内容。关于BR文件的生成,有如下三种情况:
    1.xml中设置variable的name属性
    2.viewModel继承自BaseObservable,将某个成员变量加上@Bindable注解
    3.viewModel继承自BaseObservable,开头的方法加上@Bindable注解。

      //setVariable(id,value) 最终调到了setUser()方法中
      public boolean setVariable(int variableId, @Nullable Object variable)  {
            boolean variableSet = true;
            if (BR.user == variableId) {
                setUser((com.example.databinding_demo_20200329.User) variable);
            }
            else {
                variableSet = false;
            }
                return variableSet;
        }
    

    setUser()是自己定义在XML文件当中的,自定义的name。

     <data>
            <variable
                name="user"
                type="Package.User" />
        </data>
    

    关于setUser()

     public void setUser(@Nullable User User) {
            注释①
            updateRegistration(0, User);
            this.mUser = User;
            synchronized(this) {
                mDirtyFlags |= 0x1L;
            }
            //可以去更新了。
            notifyPropertyChanged(BR.user);
            super.requestRebind();
        }
    

    注释①updateRegistration(),注册监听器。

    接着去看里面做了什么处理

    private boolean updateRegistration(int localFieldId, Object observable,
                CreateWeakListener listenerCreator) {
            if (observable == null) {
                //注释②删除监听
                return unregisterFrom(localFieldId);
            }
            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;
        }
    

    注释②:传进来的为空,则删除监听器。
    注释③:监听器为空,mLocalFieldObservers[localFieldId]中找不到这个listener,则去创建这个listener。

    接下来去看看如何注册监听的:

       protected void registerTo(int localFieldId, Object observable,
                CreateWeakListener listenerCreator) {
            if (observable == null) {
                return;
            }
            //mLocalFieldObservers 容器,用来存放WeakListener。
            WeakListener listener = mLocalFieldObservers[localFieldId];
            if (listener == null) {
                //创建监听器
                listener = listenerCreator.create(this, localFieldId);
                mLocalFieldObservers[localFieldId] = listener;
                if (mLifecycleOwner != null) {
                    listener.setLifecycleOwner(mLifecycleOwner);
                }
            }
            listener.setTarget(observable);
        }
    

    调用listenerCreator.create(this, localFieldId),会调用到下面的方法来创建监听器。

     private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new   CreateWeakListener() {
            @Override
            public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
                return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();
            }
        };
    

    这边小结一下:
    mLocalFieldObservers储存的是weakListener对象,在调用CreateWeakListener 时,会持有viewDataBinding的引用,也持有viewModel的引用,还会持有WeakPropertyListener的引用,registerTo()最后调用 listener.setTarget(observable),也就是WeakPropertyListener的addListener方法,里面又调用了viewModel的addOnPropertyChangedCallBack();

     @Override
        public void addOnPropertyChangedCallback(@NonNull OnPropertyChangedCallback callback) {
            synchronized (this) {
                if (mCallbacks == null) {
                //创建一个PropertyChangeRegistry实例
                    mCallbacks = new PropertyChangeRegistry();
                }
            }
          //把监听器设置给PropertyChangeRegistry
            mCallbacks.add(callback);
        }
    

    到这里,viewModel就持有了PropertyChangeRegistry的引用,也就建立了和ViewDataBinding的联系。
    注册完成后:


    图片.png

    如何更新数据

            updateRegistration(0, User);
            this.mUser = User;
            synchronized(this) {
                mDirtyFlags |= 0x1L;
            }
            //入口
            notifyPropertyChanged(BR.user);
            super.requestRebind();
        }
    

    注册完成后,从 notifyPropertyChanged(BR.user)来完成数据的更新。
    经过notifyPropertyChanged()->notifyCallbacks->notifyRecurse->notifyRemainder->notifyFirst64->
    notifyCallbacks->onNotifyCallback->PropertyChangeRegistry。这个看起来好熟悉,没错就是在注册监听时的那个类。

    public class PropertyChangeRegistry extends CallbackRegistry<OnPropertyChangedCallback, Observable, Void> {
        private static final NotifierCallback<OnPropertyChangedCallback, Observable, Void> NOTIFIER_CALLBACK = new NotifierCallback<OnPropertyChangedCallback, Observable, Void>() {
            public void onNotifyCallback(OnPropertyChangedCallback callback, Observable sender, int arg, Void notUsed) {
                callback.onPropertyChanged(sender, arg);
            }
        };
    
        public PropertyChangeRegistry() {
            super(NOTIFIER_CALLBACK);
        }
    
        public void notifyChange(@NonNull Observable observable, int propertyId) {
            this.notifyCallbacks(observable, propertyId, (Object)null);
        }
    }
    

    onNotifyCallback()在viewdataBinding中有实现。然会会回调handleFieldChange()方法。

     private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
            if (mInLiveDataRegisterObserver) {
                // We're in LiveData registration, which always results in a field change
                // that we can ignore. The value will be read immediately after anyway, so
                // there is no need to be dirty.
                return;
            }
            boolean result = onFieldChange(mLocalFieldId, object, fieldId);
            if (result) {
                //重新绑定,就是数据和视图重新绑定
                requestRebind();
            }
        }
    

    接着 requestRebind() -> mUIThreadHandler.post(mRebindRunnable) ->executePendingBindings()->executeBindingsInternal() -> executeBindings()【抽象方法,具体在ActivityMainBindingImpl中】;

     protected void executeBindings() {
            long dirtyFlags = 0;
            synchronized(this) {
                dirtyFlags = mDirtyFlags;
                mDirtyFlags = 0;
            }
            java.lang.String userName = null;
            com.example.databinding_demo_20200329.User user = mUser;
            java.lang.String userPassword = null;
    
            if ((dirtyFlags & 0x7L) != 0) {
    
    
    
                    if (user != null) {
                        // read user.name
                        userName = user.getName();
                    }
                if ((dirtyFlags & 0x5L) != 0) {
    
                        if (user != null) {
                            // read user.password
                            userPassword = user.getPassword();
                        }
                }
            }
            // batch finished
            if ((dirtyFlags & 0x7L) != 0) {
                // api target 1
                //发生了改变
                androidx.databinding.adapters.TextViewBindingAdapter.setText(this.tv1, userName);
            }
            if ((dirtyFlags & 0x5L) != 0) {
                // api target 1
                //发生了改变
                androidx.databinding.adapters.TextViewBindingAdapter.setText(this.tv2, userPassword);
            }
        }
        // Listener Stub Implementations
        // callback impls
        // dirty flag
        private  long mDirtyFlags = 0xffffffffffffffffL;
        /* flag mapping
            flag 0 (0x1L): user
            flag 1 (0x2L): user.name
            flag 2 (0x3L): null
        flag mapping end*/
        //end
    }
    

    这里就是一些二进制的移位运算,来判断控件的内容是否发生了改变,如果改变了重新设置一下控件的值。

    总结

    1.XML分离成两份
    2.注册监听
    3.notifyPropetyChanged方法来完成界面的更新。

    相关文章

      网友评论

          本文标题:Jetpack入门(三)viewModel介绍及dataBind

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