美文网首页Android开发
Android架构组件—ViewModel原理学习

Android架构组件—ViewModel原理学习

作者: 丘卡皮 | 来源:发表于2022-07-08 14:10 被阅读0次

    作者:Codeing_ls
    转载地址:https://juejin.cn/post/6890092603091075079

    概述

    Android ViewModel是Android Jetpack组件库中的一个组件,在Android MVVM模式中被广泛使用。ViewModel的主要特性是以注重生命周期的方式存储和管理数据。这种特性体现在ViewModel与Android组件(Activity、Fragment和Service)的生命周期相绑定,在组件的生命周期中ViewModel的数据会一直保存在内存中,即便是在系统配置变更时也会存在。

    ViewModel的优势

    与UI层低耦合

    在MVVM模式中,数据和业务逻辑可以集中在ViewModel中处理,实现功能时只需要在ViewModel中关注数据和业务即可,不需要和UI或者控件打交道。

    Fragment间共享ViewModel

    与其说是共享ViewModel,不如说是共享数据。在Android文档中有提到通过ViewModel在Fragment中共享数据,使用ViewModel共享数据可以免去Fragment之前相互回调。

    数据一直保存在内存中

    这个优势具体来讲是在系统配置发生变化导致Activity重建时,ViewModel还是会保持在内存中。在一些场景中可以通过这种方式避免重复加载数据。

    ViewModel工作原理

    在分析ViewModel的工作原理之前,我们最好带着问题去分析。

    • ViewModel如何被创建的,在什么时候被销毁;
    • ViewModel的生命周期如何与组件生命周期绑定;
    • 为什么Activity重建时ViewModel的数据没有丢失;
    • Fragment之前如何共享ViewModel;

    ViewModel的创建与销毁

    ViewModel的创建

    分析ViewModel的创建自然是从ViewModel初始化的地方开始。

    viewModel = ViewModelProvider(this).get(TestViewModel::class.java)
    

    ViewModel的初始化很简单,通过ViewModelProvider就可以获取到ViewModel实例。那么从这里入手开始分析ViewModel的创建过程。

    第一步是先初始化了一个ViewModelProvider对象,所以我们先看下ViewModelProvider的构造方法;

    public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
            this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                    ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                    : NewInstanceFactory.getInstance());
    }
    
    public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
        this(owner.getViewModelStore(), factory);
    }
    

    从ViewModelProvider的构造方法中可以看到最终是需要两个参数ViewModelStoreOwner以及Factory。这两个参数中ViewModelStoreOwner是用来存储ViewModel对象的,Factory是用来创建ViewModel对象。这个接下来的分析中也会提到。

    第二步是通过ViewModelProvider的get()方法获取ViewModel实例。

    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
            ViewModel viewModel = mViewModelStore.get(key); // 1.是否有缓存ViewModel实例缓存
    
            ...
            if (mFactory instanceof KeyedFactory) {
                viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
            } else {
                viewModel = (mFactory).create(modelClass);
            }
            mViewModelStore.put(key, viewModel);
            return (T) viewModel;
        }
    

    在get()方法中可以看到是通过mFactory的类型来创建ViewModel的。而Factory的类型是由ViewModelStoreOwner决定的,这是ViewModelProvider的构造方法中的逻辑。其中有两种Factory,一种是SavedStateViewModelFactory,另一种是NewInstanceFactory。接下来我们直接看两种方式的区别。

    SavedStateViewModelFactory创建ViewModel
    public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
            boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass);
            Constructor<T> constructor;
            ...
            SavedStateHandleController controller = SavedStateHandleController.create(
                    mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
            try {
                T viewmodel;
                if (isAndroidViewModel) {
                    viewmodel = constructor.newInstance(mApplication, controller.getHandle());
                } else {
                    viewmodel = constructor.newInstance(controller.getHandle());
                }
                viewmodel.setTagIfAbsent(TAG_SAVED_STATE_HANDLE_CONTROLLER, controller);
                return viewmodel;
            } 
            ...
        }
    

    在代码中可以看到,ViewModel有分为AndroidViewModel跟ViewModel,它们的区别是AndroidViewModel创建时会加入Application参数。

    NewInstanceFactory创建ViewModel
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
                //noinspection TryWithIdenticalCatches
                try {
                    return modelClass.newInstance();
                } catch (InstantiationException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                }
            }
    

    NewInstanceFactory创建过程相对简单,通过Class的newInstance()方法直接创建ViewModel实例。

    ViewModel的销毁

    这里还要分开两处说,一个是在Activity中的销毁过程,一个是在Fragment中销毁过程。

    首先我们把目光放到ViewModel类来看下ViewModel销毁时都做了什么。

    protected void onCleared() {}
    
    final void clear() {
        mCleared = true;
        if (mBagOfTags != null) {
            synchronized (mBagOfTags) {
                for (Object value : mBagOfTags.values()) {
                    // see comment for the similar call in setTagIfAbsent
                    closeWithRuntimeException(value);
                }
            }
        }
        onCleared();
    }
    

    可以看到ViewModel中有clear()方法和onCleared()方法。通过跟踪方法的调用可以知道ViewModel的销毁过程。最终在ViewModelStore类中找到了clear()方法。

    public final void clear() {
            for (ViewModel vm : mMap.values()) {
                vm.clear();
            }
            mMap.clear();
        }
    
    Activity中的销毁

    继续跟踪代码可以看到在ComponentActivity中:

     public ComponentActivity() {
        getLifecycle().addObserver(new LifecycleEventObserver() {
                @Override
                public void onStateChanged(@NonNull LifecycleOwner source,
                        @NonNull Lifecycle.Event event) {
                    if (event == Lifecycle.Event.ON_DESTROY) {
                        if (!isChangingConfigurations()) {
                            // 销毁ViewModel
                            getViewModelStore().clear();
                        }
                    }
                }
            });
     }
    

    在ComponentActivity的构造方法中,可以看到通过Lifecycle在ON_DESTROY事件中销毁ViewModel。

    Fragment中的销毁

    首先通过代码跟踪到ViewModelStore的clear()方法调用的地方,在FragmentManagerViewModel类的clearNonConfigState()方法中找到了ViewModel的销毁逻辑。

    void clearNonConfigState(@NonNull Fragment f) {
        ...
        // Clear and remove the Fragment's ViewModelStore
        ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
        if (viewModelStore != null) {
            // 销毁ViewModel
            viewModelStore.clear();
            mViewModelStores.remove(f.mWho);
        }
    }
    

    如果继续跟踪代码可以看到代码的调用栈是 FragmentStateManager::destroy() -> (Fragment状态切换)->FragmentManager::dispatchDestroy()->FragmentActivity::onDestory()。

    这里有个小细节需要注意,也是我在项目中遇到的一个问题。就是ViewModel的销毁在onDestory()中,是晚于onDestoryView()的,所以要注意在使用ViewModel做操作时会不会触发组件更新。不然的话可能造成空指针异常。

    ViewModel的生命周期绑定

    在上面分了ViewModel的创建与销毁过程中就可以得出ViewModel生命周期是如何与组件交互的了。主要还是通过Lifecycle和组件的生命周期方法来进行回调管理。

    Activity重建下的ViewModel

    按照上面的逻辑,在Activity重建时会执行destory生命周期事件,那么为什么ViewModel没有销毁呢?

    还是直接在代码里找答案,通过对ComponentActivity的getViewModelStore()方法进行分析。可以找到这个问题的答案。

    public ViewModelStore getViewModelStore() {
            ...
            if (mViewModelStore == null) {
                NonConfigurationInstances nc =
                        (NonConfigurationInstances) getLastNonConfigurationInstance();
                if (nc != null) {
                    // Restore the ViewModelStore from NonConfigurationInstances
                    mViewModelStore = nc.viewModelStore;
                }
                if (mViewModelStore == null) {
                    mViewModelStore = new ViewModelStore();
                }
            }
            return mViewModelStore;
        }
    

    可以看大mViewModelStore变量如果是null的话,会从NonConfigurationInstances实例中取。那么我们就分析NonConfigurationInstances实例的来源。

    从NonConfigurationInstances的名称可以大致判断这个类与配置无关。

    public final Object onRetainNonConfigurationInstance() {
            Object custom = onRetainCustomNonConfigurationInstance();
    
            ViewModelStore viewModelStore = mViewModelStore;
            if (viewModelStore == null) {
                // No one called getViewModelStore(), so see if there was an existing
                // ViewModelStore from our last NonConfigurationInstance
                NonConfigurationInstances nc =
                        (NonConfigurationInstances) getLastNonConfigurationInstance();
                if (nc != null) {
                    viewModelStore = nc.viewModelStore;
                }
            }
    
            if (viewModelStore == null && custom == null) {
                return null;
            }
    
            NonConfigurationInstances nci = new NonConfigurationInstances();
            nci.custom = custom;
            nci.viewModelStore = viewModelStore;
            return nci;
        }
    

    继续分析下去,可以看到在onRetainNonConfigurationInstance中会存储ViewModelStore实例,这也就是为什么ViewModel不会在Activity重建时被销毁的原因。

    Fragment之间共享ViewModel

    在分析ViewModel的销毁过程时,我们看到Activity与Fragment存储VieModel是分离的,那么同一个Activity下的Fragment是如何共享ViewModel的呢?

    这里先说答案,它们其实共享的是Activity的ViewModel。

    根据Android文档的代码就可以找到答案了。

     private val model: SharedViewModel by activityViewModels()
    

    这个实现的逻辑就在activityViewModels()中。

    inline fun <reified VM : ViewModel> Fragment.activityViewModels(
        noinline factoryProducer: (() -> Factory)? = null
    ) = createViewModelLazy(VM::class, { requireActivity().viewModelStore },
        factoryProducer ?: { requireActivity().defaultViewModelProviderFactory })
    

    在activityViewModels()的实现中可以看到是requireActivity()获取的viewModelStore。以此来实现共享ViewModel。

    总结

    通过上面的分析可以发现ViewModel并不算复杂,在Android其他架构组件的加持下可以说使用是很方便的。即使在项目仅仅使用(Fragment/Activity)+ViewModel也能不错的实现MVVM模式。

    相关文章

      网友评论

        本文标题:Android架构组件—ViewModel原理学习

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