Android Jetpack MVVM数据绑定及UI更新

2021-03-01  本文已影响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更新进行了详细的分析,了解一个架构的实现原理,只能通过看源码!

上一篇下一篇

猜你喜欢

热点阅读