Android安卓面试

Android:MVVM的工作原理之创建、销毁、保存、复用

2020-10-28  本文已影响0人  starryxp

前言

这里有个基础知识需要了解一下,AppCompatActivity继承了FragmentActivity继承了ComponentActivity继承了Activity,在这里的讲解我们是以继承AppCompatActivity为基础的情况下说明。

1.ViewModel创建流程

这里可以看到,初始化过程分为两步,先初始化ViewModelProvider,我们来看一下源码:

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

这里的ViewModelStoreOwner就传入当前的AppCompatActivity或者Fragment就可以了,我们看一下ViewModelStoreOwner源码:

public interface ViewModelStoreOwner {
    /**
     * Returns owned {@link ViewModelStore}
     *
     * @return a {@code ViewModelStore}
     */
    @NonNull
    ViewModelStore getViewModelStore();
}

他是一个接口,Fragment,FragmentActivity等都实现了这个接口。下面我们来看一下ViewModelStore是什么:

public class ViewModelStore {

    private final HashMap<String, ViewModel> mMap = new HashMap<>();

    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }

    final ViewModel get(String key) {
        return mMap.get(key);
    }

    /**
     *  Clears internal storage and notifies ViewModels that they are no longer used.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.onCleared();
        }
        mMap.clear();
    }
}

ViewModelStore里面有一个HashMap,这个HashMap是用来存放ViewModel,还提供了一个clear()方法,这个方法里的操作很简单,就是调用存放ViewModel的onCleared()方法,以及清空HashMap。ViewModel其实很简单,就是个抽象类,里面有个onCleared()方法。

下面我们来看看ViewModelProvider(ViewModelStoreOwner)随后调用了ViewModelProvider(ViewModelStore, Factory)方法,我们来看一下getViewModelStore()的源码,是如何创建ViewModelStore的,这里我们看Activity的:

    public ViewModelStore getViewModelStore() {
        if (getApplication() == null) {
            throw new IllegalStateException("Your activity is not yet attached to the "
                    + "Application instance. You can't request ViewModel before onCreate call.");
        }
        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;
    }

这里我们可以看到,先判断是否需要还原ViewModelStore,这个后面会再讲到;然后就是实例化一个ViewModelStore返回。下面我们来看一下Factory是怎么来的:

owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance()

可以看到,他会判断传入的activity是否有是HasDefaultViewModelProviderFactory,他是一个接口,ComponentActivity、Fragment都实现了这个接口,我们看一下ComponentActivity中getDefaultViewModelProviderFactory()方法:

    public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
        if (getApplication() == null) {
            throw new IllegalStateException("Your activity is not yet attached to the "
                    + "Application instance. You can't request ViewModel before onCreate call.");
        }
        if (mDefaultFactory == null) {
            mDefaultFactory = new SavedStateViewModelFactory(
                    getApplication(),
                    this,
                    getIntent() != null ? getIntent().getExtras() : null);
        }
        return mDefaultFactory;
    }

继续看SavedStateViewModelFactory()方法:

    public SavedStateViewModelFactory(@NonNull Application application,
            @NonNull SavedStateRegistryOwner owner,
            @Nullable Bundle defaultArgs) {
        mSavedStateRegistry = owner.getSavedStateRegistry();
        mLifecycle = owner.getLifecycle();
        mDefaultArgs = defaultArgs;
        mApplication = application;
        mFactory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }

可以看到这个Factory就是ViewModelProvider.AndroidViewModelFactory单列

下面我们再来看get(vm.class)方法:

    public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
        String canonicalName = modelClass.getCanonicalName();
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }

继续:

    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);

        if (modelClass.isInstance(viewModel)) {
            if (mFactory instanceof OnRequeryFactory) {
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

这里使用mFactory创建出viewModel,调用就是AndroidViewModelFactory的create()方法:

        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
                //noinspection TryWithIdenticalCatches
                try {
                    return modelClass.getConstructor(Application.class).newInstance(mApplication);
                } catch (NoSuchMethodException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (InstantiationException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (InvocationTargetException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                }
            }
            return super.create(modelClass);
        }

然后将viewModel存储到ViewModelStore中,到这里ViewModel就创建完成了。

2.ViewModel的生命周期

image.png

这张ViewModel生命周期图时谷歌官方提供的,从这上面显示activity在横竖屏旋转重建时ViewModel也是一直存在内存中。

经过ViewModel创建的源码分析,我们知道ViewModel是存放在ViewModelStore中的,ViewModelStore有clear()方法用来清除存放的ViewModel,我们先来找一下这个ViewModelStore.clear()在哪里调用:

Fragment中销毁
    public void onDestroy() {
        mCalled = true;
        FragmentActivity activity = getActivity();
        boolean isChangingConfigurations = activity != null && activity.isChangingConfigurations();
        if (mViewModelStore != null && !isChangingConfigurations) {
            mViewModelStore.clear();
        }
    }

这里是在onDestroy()方法中调用的,注意这里有个判断!isChangingConfigurations(),这是横竖屏切换时不会去销毁ViewModel。

FragmentActivity中销毁
    protected void onDestroy() {
        super.onDestroy();

        if (mViewModelStore != null && !isChangingConfigurations()) {
            mViewModelStore.clear();
        }

        mFragments.dispatchDestroy();
    }

这里还是在onDestroy()方法中调用的,且横竖屏切换时不会去销毁ViewModel。

这里可以知道当页面销毁的时候ViewModel也是会被销毁的,但是横竖屏切换不去销毁,那么按照常理来讲不销毁就是想复用,也就是说在Activity横竖屏切换重建页面的时候,ViewModel是不销毁的需要复用,那么我们就来找找他是如何复用的。

FragmentActivity中恢复

    protected void onCreate(@Nullable Bundle savedInstanceState) {
        mFragments.attachHost(null /*parent*/);

        super.onCreate(savedInstanceState);

        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null && nc.viewModelStore != null && mViewModelStore == null) {
            mViewModelStore = nc.viewModelStore;
        }
        if (savedInstanceState != null) {
            Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
            mFragments.restoreAllState(p, nc != null ? nc.fragments : null);
            ......
        }
        ......
    }

在onCreate的时候会从NonConfigurationInstances中将ViewModelStore恢复出来,这段恢复的代码是不是有点熟悉,在创建的时候getViewModelStore()方法中有一样的代码。同时在这里我们也看到了Fragment保存状态的恢复,这些Fragment保存状态中就包含了ViewModelStore,这里关于Fragment的我们稍后再看,先看Activity的。

现在我们找到了ViewModelStore在重建Activity中恢复的地方了,那就开始寻找是在哪里保存的呢。

FragmentActivity中保存
    /**
     * Retain all appropriate fragment state.  You can NOT
     * override this yourself!  Use {@link #onRetainCustomNonConfigurationInstance()}
     * if you want to retain your own state.
     */
    @Override
    public final Object onRetainNonConfigurationInstance() {
        Object custom = onRetainCustomNonConfigurationInstance();

        FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();

        if (fragments == null && mViewModelStore == null && custom == null) {
            return null;
        }

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = mViewModelStore;
        nci.fragments = fragments;
        return nci;
    }

在这里我们看到了Activity的ViewModelStore被保存在了NonConfigurationInstances中,之前在恢复的时候我们也看到了是从NonConfigurationInstances中恢复出来的,,同时也找到了Fragment信息的保存代码,关于Fragment的同样是后面再看。这里有个小知识点,从官方注释可以知道,我们如果需要做自定义的数据保存需要重写onRetainCustomNonConfigurationInstance()而不是onRetainNonConfigurationInstance()这点要注意。

剩下的就跟Activity的启动流程有关了,这里不详细说明,就大概说一下。在ActivityThread调用performDestroyActivity()方法的时候会调用retainNonConfigurationInstances()方法将NonConfigurationInstances保存到ActivityClientRecord中,即lastNonConfigurationInstances属性上,然后在ActivityThread 调用performLaunchActivity()方法执行Activity.attach()方法的时候传递给重建的新的Activity。

Fragment保存

下面我们来看一下Fragment中是怎么保存ViewModel的。之前在看Activity的时候,我们就看到了FragmentManagerNonConfig信息保存和恢复的代码,Fragment的相关内容就在这里面。

这里我们使用反向寻找的办法,从Activity中保存的代码中向上寻找会比较简单一点。
还记得在FragmentActivity中onRetainNonConfigurationInstance()中的代码吗:

FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();

跟下去,在FragmentController中:

    public FragmentManagerNonConfig retainNestedNonConfig() {
        return mHost.mFragmentManager.retainNonConfig();
    }

这里的mFragmentManager是FragmentManagerImpl,是FragmentManager的现实类。继续,FragmentManager中:

    FragmentManagerNonConfig retainNonConfig() {
        setRetaining(mSavedNonConfig);
        return mSavedNonConfig;
    }

继续下去:

    private static void setRetaining(FragmentManagerNonConfig nonConfig) {
        if (nonConfig == null) {
            return;
        }
        List<Fragment> fragments = nonConfig.getFragments();
        if (fragments != null) {
            for (Fragment fragment : fragments) {
                fragment.mRetaining = true;
            }
        }
        List<FragmentManagerNonConfig> children = nonConfig.getChildNonConfigs();
        if (children != null) {
            for (FragmentManagerNonConfig child : children) {
                setRetaining(child);
            }
        }
    }

这里可以看到,使用递归的方式将mSavedNonConfig其中fragment.mRetaining = true,然后将mSavedNonConfig返回。下面我们来看看mSavedNonConfig是什么,怎么来的:

    // Saved FragmentManagerNonConfig during saveAllState() and cleared in noteStateNotSaved()
    FragmentManagerNonConfig mSavedNonConfig;

这里官方有解释,大概意思就是在saveAllState()保存FragmentManagerNonConfig,并在noteStateNotSaved()中清除。那就很简单了,找saveAllState()方法,这个方法特别长,这里只展示跟saveNonConfig有关的部分代码:

    Parcelable saveAllState() {
        ......
        mStateSaved = true;
        mSavedNonConfig = null;
        ......
        saveNonConfig();
        return fms;
    }

继续看saveNonConfig()方法,这里我们就展示跟ViewModelStore有关的代码了:

    void saveNonConfig() {
        ArrayList<Fragment> fragments = null;
        ArrayList<FragmentManagerNonConfig> childFragments = null;
        ArrayList<ViewModelStore> viewModelStores = null;
        if (mActive != null) {
            for (int i=0; i<mActive.size(); i++) {
                    ......
                    if (viewModelStores == null && f.mViewModelStore != null) {
                        viewModelStores = new ArrayList<>(mActive.size());
                        for (int j = 0; j < i; j++) {
                            viewModelStores.add(null);
                        }
                    }

                    if (viewModelStores != null) {
                        viewModelStores.add(f.mViewModelStore);
                    }
                }
            }
        }
        if (fragments == null && childFragments == null && viewModelStores == null) {
            mSavedNonConfig = null;
        } else {
            mSavedNonConfig = new FragmentManagerNonConfig(fragments, childFragments,
                    viewModelStores);
        }
    }

这里的mActive是一个SparseArray<Fragment>,for循环将其中的所有Fragment的ViewModelStore存放到ArrayList<ViewModelStore> viewModelStores,然后在实例化一个FragmentManagerNonConfig赋值给mSavedNonConfig。那么这条线就通了,Fragment的ViewModel最后是用过FragmentManagerNonConfig交给了NonConfigurationInstances进行保存的。那saveAllState()又是谁来调用的呢,是由FragmentController来管理的,在FragmentActivity调用onSaveInstanceState()方法的时候就会同时保存Fragment的状态。

Fragment恢复

还记得FragmentActivity中的恢复代码吗:

mFragments.restoreAllState(p, nc != null ? nc.fragments : null);

这里从NonConfigurationInstances中拿到了需要恢复的FragmentManagerNonConfig调用了FragmentController的restoreAllState()方法:

    public void restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) {
        mHost.mFragmentManager.restoreAllState(state, nonConfig);
    }

继续,这里代码也很多,我们只看ViewModelStore相关的就行:

    void restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) {
        ......
        List<FragmentManagerNonConfig> childNonConfigs = null;
        List<ViewModelStore> viewModelStores = null;
        if (nonConfig != null) {
            List<Fragment> nonConfigFragments = nonConfig.getFragments();
            childNonConfigs = nonConfig.getChildNonConfigs();
            viewModelStores = nonConfig.getViewModelStores();
            ......
        }
        mActive = new SparseArray<>(fms.mActive.length);
        for (int i=0; i<fms.mActive.length; i++) {
            FragmentState fs = fms.mActive[i];
            if (fs != null) {
                ......
                ViewModelStore viewModelStore = null;
                if (viewModelStores != null && i < viewModelStores.size()) {
                    viewModelStore = viewModelStores.get(i);
                }
                Fragment f = fs.instantiate(mHost, mContainer, mParent, childNonConfig,
                        viewModelStore);
                ......
            }
        }
        ......
    }

这里还是比较好理解的,大概就是从FragmentManagerNonConfig取出保存的List<ViewModelStore>,然后循环将ViewModelStore赋值到原先对应的Fragment上。

至此Activity、Fragment创建,销毁,保存,复用ViewModel的流程就基本清楚了。

上一篇 下一篇

猜你喜欢

热点阅读