zoukankan      html  css  js  c++  java
  • ViewModel和LiveData问题思考与解答

    嗨,大家好,面试真题系列又来了,今天我们说说MVVM架构里的两大组件:ViewModel和LiveData。
    还是老样子,提出问题,做出解答。

    • ViewModel 是什么?

    • ViewModel 为什么被设计出来,解决了什么问题?

    • 说说ViewModel原理。

    • LiveData 是什么?

    • LiveData 为什么被设计出来,解决了什么问题?

    • 说说LiveData原理。

    ViewModel 是什么,说说你所理解的ViewModel?

    如果看过我上一篇文章的小伙伴应该都有所了解,ViewModel是MVVM架构的一个层级,用来联系View和model之间的关系。而我们今天要说的就是官方出的一个框架——ViewModel

    ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据

    官方是这么介绍的,这里面有两个信息:

    • 注重生命周期的方式。
      由于ViewModel的生命周期是作用于整个Activity的,所以就节省了一些关于状态维护的工作,最明显的就是对于屏幕旋转这种情况,以前对数据进行保存读取,而ViewModel则不需要,他可以自动保留数据。

    其次,由于ViewModel在生命周期内会保持局部单例,所以可以更方便Activity的多个Fragment之间通信,因为他们能获取到同一个ViewModel实例,也就是数据状态可以共享了。

    • 存储和管理界面相关的数据。

    ViewModel层的根本职责,就是负责维护界面上UI的状态,其实就是维护对应的数据,因为数据会最终体现到UI界面上。所以ViewModel层其实就是对界面相关的数据进行管理,存储等操作。

    ViewModel 为什么被设计出来,解决了什么问题?

    • ViewModel组件被设计出来之前,MVVM又是怎么实现ViewModel这一层级的呢?

    其实就是自己编写类,然后通过接口,内部依赖实现View和数据的双向绑定。
    所以Google出这个ViewModel组件,无非就是为了规范MVVM架构的实现,并尽量让ViewModel这一层级只触及到业务代码,不去关心VIew层级的引用等。然后配合其他的组件,包括livedata,databindingrang等让MVVM架构更加完善,规范,健硕。

    • 解决了什么问题呢?

    其实上面已经说过一些了,比如:

    1)不会因为屏幕旋转而销毁,减少了维护状态的工作
    2)由于在作用域内单一实例的特性,使得多个fragment之间可以方便通信,并且维护同一个数据状态。
    3)完善了MVVM架构,使得解耦更加纯粹。

    说说ViewModel原理。

    • 首先说说是怎么保存生命周期

    ViewModel2.0之前呢,其实原理是在Activity上add一个HolderFragment,然后设置setRetainInstance(true)方法就能让这个Fragment在Activity重建时存活下来,也就保证了ViewModel的状态不会随Activity的状态所改变。

    2.0之后,其实是用到了Activity的onRetainNonConfigurationInstance()getLastNonConfigurationInstance()这两个方法,相当于在横竖屏切的时候会保存ViewModel的实例,然后恢复,所以也就保证了ViewModel的数据。

    • 再说说怎么保证作用域内唯一实例

    首先,ViewModel的实例是通过反射获取的,反射的时候带上application的上下文,这样就保证了不会持有Activity或者Fragment等View的引用。然后实例创建出来会保存到一个ViewModelStore容器里面,其实也就是一个集合类,这个ViewModelStore 类其实就是保存在界面上的那个实例,而我们的ViewModel就是里面的一个集合类的子元素。

    所以我们每次获取的时候,首先看看这个集合里面有无我们的ViewModel,如果没有就去实例化,如果有就直接拿到实例使用,这样就保证了唯一实例。最后在界面销毁的时候,会去执行ViewModelStore的clear方法,去清除集合里面的ViewModel数据。一小段代码说明下:

    public <T extends ViewModel> T get(Class<T> modelClass) {
          // 先从ViewModelStore容器中去找是否存在ViewModel的实例
          ViewModel viewModel = mViewModelStore.get(key);
         
          // 若ViewModel已经存在,就直接返回
          if (modelClass.isInstance(viewModel)) {
                return (T) viewModel;
          }
           
          // 若不存在,再通过反射的方式实例化ViewModel,并存储进ViewModelStore
          viewModel = modelClass.getConstructor(Application.class).newInstance(mApplication);
          mViewModelStore.put(key, viewModel);
          return (T) viewModel;
     }
    
    
    public class ViewModelStore {
        private final HashMap<String, ViewModel> mMap = new HashMap<>();
    
         public final void clear() {
            for (ViewModel vm : mMap.values()) {
                vm.onCleared();
            }
            mMap.clear();
        }
    }
    
    
    
     @Override
    protected void onDestroy() {
        super.onDestroy();
    
       if (mViewModelStore != null && !isChangingConfigurations()) {
            mViewModelStore.clear();
        }
    
    }
    
    

    LiveData 是什么?

    LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。

    官方介绍如下,其实说的比较清楚了,主要作用在两点:

    • 数据存储器类。也就是一个用来存储数据的类。

    • 可观察。这个数据存储类是可以观察的,也就是比一般的数据存储类多了这么一个功能,对于数据的变动能进行响应。

    主要思想就是用到了观察者模式思想,让观察者和被观察者解耦,同时还能感知到数据的变化,所以一般被用到ViewModel中,ViewModel负责触发数据的更新,更新会通知到LiveData,然后LiveData再通知活跃状态的观察者。

            var liveData = MutableLiveData<String>()
    
            liveData.observe(this, object : Observer<String> {
                override fun onChanged(t: String?) {
                }
            })
    
            liveData.setVaile("xixi")
            //子线程调用
            liveData.postValue("test")
    

    LiveData 为什么被设计出来,解决了什么问题?

    LiveData作为一种观察者模式设计思想,常常被和Rxjava一起比较,观察者模式的最大好处就是事件发射的上游 和 接收事件的下游 互不干涉,大幅降低了互相持有的依赖关系所带来的强耦合性

    其次,LiveData还能无缝衔接到MVVM架构中,主要体现在其可以感知到Activity等生命周期,这样就带来了很多好处:

    • 不会发生内存泄漏
      观察者会绑定到 Lifecycle 对象,并在其关联的生命周期遭到销毁后进行自我清理。

    • 不会因 Activity 停止而导致崩溃
      如果观察者的生命周期处于非活跃状态(如返回栈中的 Activity),则它不会接收任何 LiveData 事件。

    • 自动判断生命周期并回调方法
      如果观察者的生命周期处于 STARTEDRESUMED状态,则 LiveData 会认为该观察者处于活跃状态,就会调用onActive方法,否则,如果 LiveData 对象没有任何活跃观察者时,会调用 onInactive()方法。

    说说LiveData原理。

    说到原理,其实就是两个方法:

    • 订阅方法,也就是observe方法。通过该方法把订阅者和被观察者关联起来,形成观察者模式。

    简单看看源码:

        @MainThread
        public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
            assertMainThread("observe");
            //...
            LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
            ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
            if (existing != null && !existing.isAttachedTo(owner)) {
                throw new IllegalArgumentException("Cannot add the same observer"
                        + " with different lifecycles");
            }
            if (existing != null) {
                return;
            }
            owner.getLifecycle().addObserver(wrapper);
        }
    
          public V putIfAbsent(@NonNull K key, @NonNull V v) {
            Entry<K, V> entry = get(key);
            if (entry != null) {
                return entry.mValue;
            }
            put(key, v);
            return null;
        }
    

    这里putIfAbsent方法是讲生命周期相关的wrapper和观察者observer作为key和value存到了mObservers中。

    • 回调方法,也就是onChanged方法。通过改变存储值,来通知到观察者也就是调用onChanged方法。从改变存储值方法setValue看起:
    @MainThread
    protected void setValue(T value) {
        assertMainThread("setValue");
        mVersion++;
        mData = value;
        dispatchingValue(null);
    }
    
    
    private void dispatchingValue(@Nullable ObserverWrapper initiator) {
        //...
        do {
            mDispatchInvalidated = false;
    
            if (initiator != null) {
                considerNotify(initiator);
                initiator = null;
            } else {
                for (Iterator<Map.Entry<Observer<T>, ObserverWrapper>> iterator =
                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                    considerNotify(iterator.next().getValue());
                    if (mDispatchInvalidated) {
                        break;
                    }
                }
            }
        } while (mDispatchInvalidated);
        mDispatchingValue = false;
    }
    
    
    private void considerNotify(ObserverWrapper observer) {
        if (!observer.mActive) {
            return;
        }
        // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
        //
        // we still first check observer.active to keep it as the entrance for events. So even if
        // the observer moved to an active state, if we've not received that event, we better not
        // notify for a more predictable notification order.
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        //noinspection unchecked
        observer.mObserver.onChanged((T) mData);
    }
    

    这一套下来逻辑还是比较简单的,遍历刚才的map——mObservers,然后找到观察者observer,如果观察者不在活跃状态(活跃状态,也就是可见状态,处于 STARTED 或 RESUMED状态),则直接返回,不去通知。否则正常通知到观察者的onChanged方法。

    当然,如果想任何时候都能监听到,都能获取回调,调用observeForever方法即可。

    参考

    viewmodel推荐阅读
    livedata推荐阅读

    拜拜

    有一起学习的小伙伴可以关注下❤️我的公众号——码上积木,每天三问面试真题,详细剖析,助你成为offer收割机。

  • 相关阅读:
    x64 平台开发 Mapxtreme 编译错误
    hdu 4305 Lightning
    Ural 1627 Join(生成树计数)
    poj 2104 Kth Number(可持久化线段树)
    ural 1651 Shortest Subchain
    hdu 4351 Digital root
    hdu 3221 Bruteforce Algorithm
    poj 2892 Tunnel Warfare (Splay Tree instead of Segment Tree)
    hdu 4031 Attack(BIT)
    LightOJ 1277 Looking for a Subsequence
  • 原文地址:https://www.cnblogs.com/jimuzz/p/13913179.html
Copyright © 2011-2022 走看看