美文网首页
Android Jetpack MVVM数据绑定及UI更新

Android Jetpack MVVM数据绑定及UI更新

作者: 雷涛赛文 | 来源:发表于2021-03-01 12:50 被阅读0次

    一.Jetpack简介

          2018年谷歌I/O大会发布了一系列辅助Android开发者的实用工具,合称Jetpack,以帮助开发者构建出色的 Android 应用。
          按照Google官方的说法,“Jetpack是一套库、工具和指南,可以帮助开发者更轻松地编写应用程序。Jetpack中的组件可以帮助开发者遵循最佳做法、摆脱编写样板代码的工作并简化复杂的任务,以便他们能将精力集中放在业务所需的代码上”。

    Jetpack与AndroidX

          在2018年的Google I/O大会上,Google宣布用AndroidX代替Android Support Library,AndroidSupport Library在版本28之后就不再更新了,未来的更新会在AndroidX中进行。不仅如此,AAC(Android Architecture Component)中的组件也被并入AndroidX。所以,当使用Jetpack的组件时,经常会看到以“androidx”开头的包名,Android Support Library与AAC中的各种组件已经迁移到了AndroidX中。
          为什么Jetpack组件需要以兼容包的形式存在,而不是成为Framework的一部分呢?很简单,这是为了提供向后兼容,使Jetpack组件能够应对更加频繁的更新。除了Android Support Library和AAC,其他一些需要频繁更新和迭代的特性也被并入了AndroidX。
          我们平常使用的DataBinding,LifeCycle,ViewModel,LiveData等都是Jetpack旗下的,之前也做过相应的分析:
          Android Jetpack ViewModel详解
          Android Jetpack Lifecycle详解
          Android Jetpack LiveData原理分析
          Android JetPack DataBinding分析
          平常在开发的时候,都是会将其一起使用,比如:MVVM架构,使用DataBinding+LiveData+LifeCycle+ViewModel来进行实现,各司其职,将MVVM架构脱颖而出,本文通过一个MVVM实例来分析一下数据绑定及UI更新的实现原理。

    二.MVVM实例

          结合MVVM实例来分析一下ViewModel在MVVM中起了什么作用,LiveData是如何与UI进行绑定及UI如何更新。

    a.View
    public class ViewModelLiveDataFragment extends BaseFragment {
    
        private MainViewModel mMainViewModel;
    
        @Override
        public int getLayoutId() {
            return R.layout.livedata_layout;
        }
    
        @Override
        public void initData(View view) {
    
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            LivedataLayoutBinding binding = DataBindingUtil.inflate(inflater, R.layout.livedata_layout, container, false);
            View view = binding.getRoot();
            mMainViewModel = new ViewModelProvider(this.getViewModelStore(), new ViewModelProvider.NewInstanceFactory()).get(MainViewModel.class);
            binding.setViewModel(mMainViewModel);
            //通过以下逻辑来保证MutableLiveData变化时来更新UI
            //该方法中最终会调用到observe()方法
            binding.setLifecycleOwner(this);
            return view;
        }
    }
    

          以上可以看到,在onCreateView()时,去new一个ViewModelProvider(),然后通过来get()传入类来获取MainViewModel,用来存储数据。

    b.ViewModel
    public class MainViewModel extends ViewModel {
    
        private ImageDepository mImageDepository;
    
        public MainViewModel() {
            mImageDepository = new ImageDepository();
            step.setValue(1);
            getImage(step.getValue());
        }
    
        public MutableLiveData<Integer> step = new MutableLiveData<>();
        public MutableLiveData<String> imageUrl = new MutableLiveData<>();
        public MutableLiveData<String> imageDescription = new MutableLiveData<>();
    
        public void onClick(View view) {
            if (view.getId() == R.id.up) {
                step.setValue(step.getValue() - 1);
            } else if (view.getId() == R.id.down) {
                step.setValue(step.getValue() + 1);
            }
            getImage(step.getValue());
        }
    
        private void getImage(int step) {
            Observable<ImageBean> observable = mImageDepository.getImage("js", step, 1);
            observable.subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Observer<ImageBean>() {
                        @Override
                        public void onSubscribe(Disposable d) {
                        }
    
                        @Override
                        public void onNext(ImageBean imageBean) {
                            List<ImageBean.ImagesBean> imagesBeans = imageBean.getImages();
                            ImageBean.ImagesBean imagesBean = imagesBeans.get(0);
                            String url = ImageBean.ImagesBean.BASE_URL + imagesBean.getUrl();
                            String des = imagesBean.getCopyright();
                            imageUrl.setValue(url);
                            imageDescription.setValue(des);
                        }
    
                        @Override
                        public void onError(Throwable e) {
    
                        }
    
                        @Override
                        public void onComplete() {
                        }
            });
        }
    }
    

          以上可以看到,MainViewModel内部定义了许多LiveData变量,如果MainViewModel是唯一的,那么LiveData也就被存储下来,当UI从后台处于前台时,可以将最新值同步更新到UI。

    c.M
    public class ImageDepository {
    
        private RetrofitApi mRetrofitApi;
    
        public ImageDepository() {
            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl("https://cn.bing.com/")
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .build();
            mRetrofitApi = retrofit.create(RetrofitApi.class);
        }
    
        public Observable<ImageBean> getImage(String format, int idx, int n) {
            return mRetrofitApi.getImage(format, idx, n);
        }
    }
    

          M就是数据模块,用来获取数据,比如:用Retrofit请求网络数据等。
          MVVM中,跟UI绑定的是LiveData,接下来看一下LiveData是如何与UI进行绑定的。

    三.LiveData与UI进行绑定

          在工程的generated目录下会生成xxBindingImpl.java文件,该文件实现xxViewDataBinding类,xxViewDataBinding类实现ViewDataBinding类。

    public class LivedataLayoutBindingImpl extends LivedataLayoutBinding  {
        private LivedataLayoutBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
            //调用父类的构造方法
            super(bindingComponent, root, 3
                , (android.widget.TextView) bindings[1]
                , (android.widget.TextView) bindings[4]
                , (android.widget.TextView) bindings[3]
                );
            this.description.setTag(null);
            this.down.setTag(null);
            this.mboundView0 = (android.widget.RelativeLayout) bindings[0];
            this.mboundView0.setTag(null);
            this.mboundView2 = (android.widget.ImageView) bindings[2];
            this.mboundView2.setTag(null);
            this.up.setTag(null);
            setRootTag(root);
            // listeners
            invalidateAll();
        }
    
        @Override
        public void invalidateAll() {
            synchronized(this) {
                    mDirtyFlags = 0x10L;
            }
            requestRebind();
        }
    
        @Override
        protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
            ......
        }
    
        @Override
        protected void executeBindings() {
           ........
           ........
            // batch finished
            if ((dirtyFlags & 0x1cL) != 0) {
                // api target 1
                //更新UI
                androidx.databinding.adapters.TextViewBindingAdapter.setText(this.description, viewModelImageDescriptionGetValue);
            }
            ......
            ......
    }
    

          MutableLiveData是如何和UI进行绑定的?
          1.xxBindingImpl类的构造方法,会最终调用ViewDataBinding类的构造方法,会根据MutableLiveData的数量来初始化创建对应数量的WeakListener,后续LiveData value变化会通知UI更新。
          2.在xxBindingImpl构造方法中,执行了以下调用逻辑:
          ------>invalidateAll()
                ------>requestRebind()
                      ------>.......
                            ----->executePendingBindings()
                                  ------>executeBindingsInternal()
                                        ------>executeBindings(),
    在executeBindings()里面来注册及更新UI。
          3.在executeBindings()里面有一个updateLiveDataRegistration(0, viewModelImageUrl),此方法作为注册回调的入口,将对应的LiveData进行observe(),调用关系为:
          ------>updateRegistration(0, viewModelImageUrl, CREATE_LIVE_DATA_LISTENER)
                ------>registerTo()
                      ------>listener.setTarget(viewModelImageUrl);
          在registerTo()方法里面,获取到对应Observable[LiveData]的weakListener,然后将其赋值给步骤1创建的WeakListener数组里对应的值。

    /**
     * Method object extracted out to attach a listener to a bound LiveData object.
     */
    private static final CreateWeakListener CREATE_LIVE_DATA_LISTENER = new CreateWeakListener() {
        @Override
        public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
            return new LiveDataListener(viewDataBinding, localFieldId).getListener();
        }
    };
    
    private static class LiveDataListener implements Observer,
                ObservableReference<LiveData<?>> {
            final WeakListener<LiveData<?>> mListener;
            LifecycleOwner mLifecycleOwner;
    
            public LiveDataListener(ViewDataBinding binder, int localFieldId) {
                //创建WeakListener时把自己作为Observable传进去,后续执行在WeakListener中执行setLifecycleOwner会用到
                mListener = new WeakListener(binder, localFieldId, this);
            }
    
            @Override
            public void setLifecycleOwner(LifecycleOwner lifecycleOwner) {
                LifecycleOwner owner = (LifecycleOwner) lifecycleOwner;
                LiveData<?> liveData = mListener.getTarget();
                if (liveData != null) {
                    if (mLifecycleOwner != null) {
                        liveData.removeObserver(this);
                    }
                    if (lifecycleOwner != null) {
                        //执行observe,主要做了两项操作:
                        //1.自身监听liveData的变化,有变化执行onChanged();
                        //2.liveData监听LifeCyclerOwner生命周期状态的变化
                        liveData.observe(owner, this);
                    }
                }
                mLifecycleOwner = owner;
            }
    
            @Override
            public WeakListener<LiveData<?>> getListener() {
                return mListener;
            }
    
            @Override
            public void addListener(LiveData<?> target) {
                if (mLifecycleOwner != null) {
                    target.observe(mLifecycleOwner, this);
                }
            }
    
            @Override
            public void removeListener(LiveData<?> target) {
                target.removeObserver(this);
            }
    
           //mutableLiveData变化时会回调该方法
            @Override
            public void onChanged(@Nullable Object o) {
                ViewDataBinding binder = mListener.getBinder();
                if (binder != null) {
                    binder.handleFieldChange(mListener.mLocalFieldId, mListener.getTarget(), 0);
                }
            }
        }
    
    

          4.后续会执行binding.setLifecycleOwner(this),在该方法中将在步骤1中创建的WeakListener分别执行setLifecycleOwner(this),会执行LiveDataListener.setsetLifecycleOwner(this)------>liveData.observe(owner, this),此方法主要做了两个操作:
          ①.MutableLiveData建立了对LifecycleOwner(activity/Fragment)生命周期的变化监听;
          ②.建立了对MutableLiveData的值变化监听;
          总的执行逻辑图如下:


    image.png

          以上已经分析了LiveData是如何与UI进行绑定的,接下来看一下当LiveData发生变化后,是如何通知UI进行更新的。

    四.LiveData如何通知UI更新

          MutableLiveData变化后如何进行UI更新?

        protected void setValue(T value) {
            assertMainThread("setValue");
            mVersion++;
            mData = value;
            dispatchingValue(null);
        }
    

          通过setValue()来对数据进行更新,先确保是主线程;然后将mVersion++,此处操作是为了避免重复发送更新;其次将value值进行存储在mData中,后续owner的生命周期变为active后,会利用到mData进行更新;最后执行dispatchingValue()进行实时更新。接下来看一下dispatchingValue()的实现:

    void dispatchingValue(@Nullable ObserverWrapper initiator) {
            if (mDispatchingValue) {
                mDispatchInvalidated = true;
                return;
            }
            mDispatchingValue = true;
            do {
                mDispatchInvalidated = false;
                if (initiator != null) {
                    considerNotify(initiator);
                    initiator = null;
                } else {
                    for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                            mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                        considerNotify(iterator.next().getValue());
                        if (mDispatchInvalidated) {
                            break;
                        }
                    }
                }
            } while (mDispatchInvalidated);
            mDispatchingValue = false;
        }
    

           当initiator不为null时,是单独更新的,主要适应于owner生命周期状态变化时进行通知,由于传入的initiator为null,那么会执行else逻辑,遍历mObservers来进行通知observer进行更新UI,即调用到considerNotify(),看一下considerNotify()的实现:

        private void considerNotify(ObserverWrapper observer) {
            //如果observer不处于active,则不去更新,从而不会造成内存泄露
            if (!observer.mActive) {
                return;
            }
         
            if (!observer.shouldBeActive()) {
                observer.activeStateChanged(false);
                return;
            }
            if (observer.mLastVersion >= mVersion) {
                return;
            }
            observer.mLastVersion = mVersion;
            //回调observer的onChanged()更新UI
            observer.mObserver.onChanged((T) mData);
        }
    

          首先判断observer是否为active,否则直接返回;然后判断mLastVersion >= mVersion,避免重复更新,每次更新后都进行赋值;最后执行observer的onChanged()来进行回调更新。
    简单总结一下:
          1.MutableLiveData进行setValue()后,执行的调用关系如下:
          ------>dispatchingValue(null)
                ----->considerNotify()
                      ------>observerWrapper.mObserver.onChanged((T) mData);
                            ------>LiveDataListener.onChanged()
          2.根据上述绑定UI的第4步observe()时传入的observer,会回调LiveDataListener里面的onChanged()方法。
          3.onChanged()方法中会调用handleFieldChange(),会先调用xxBindingImpl中的onFieldChange()方法,然后调用requestRebind()------>.....------>调用xxBindingImpl中的executeBindings()来更新UI,流程图如下:

    image.png

          以上就是针对一个MVVM实例,对内部的数据绑定及UI更新进行了详细的分析,了解一个架构的实现原理,只能通过看源码!

    相关文章

      网友评论

          本文标题:Android Jetpack MVVM数据绑定及UI更新

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