Android架构组件之ViewModel源码解析

ViewModel是Google官方MVVM架构的核心组件之一。同时,在Google推出的Android Jetpack组件中,也将ViewModel放在了Architecture类别之中。ViewModel类旨在以一种有生命周期的方式存储和管理与UI相关的数据,并且不会因为屏幕旋转等配置变化后而销毁。


Android架构组件之ViewModel源码解析_第1张图片
ViewModel生命周期

从ViewModel生命周期图中可以看出我们发现,当activity因屏幕旋转而销毁,但是ViewModel一直存在,正是因为ViewModel有如此的生命周期,所以ViewModel在MVVM中可以作为数据存储区,是连接View和Model重要组件。
本文主要通过分析ViewModel源码,搞清楚如下几个问题:

  • ViewModel是如何创建的?
  • ViewModel是如何存储的,具体放在哪?
  • ViewModel如何实现旋转屏幕而不销毁?

一、ViewModel创建过程

使用过ViewModel的同学都应该知道,当我们想要创建一个ViewModel对象的时候,不是通过new关键字去创建的,而是通过ViewModelProviders.of(this).get(ViewModel.class)这种调用方式创建的,简言之就是通过ViewModelProviders去创建。其实,我们想想也可以知道,ViewModel想要有生命周期,那么它势必就要和我们的Activity或者Fragment相关联起来,至于如何关联,源码会给我们答案。

我们先来看下ViewModelProviders.of()方法,调用这个方法最终会走到下面这2个方法其中之一,一个是在Activity里面调用,另一个则是在Fragment中调用:

    public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
        Application application = checkApplication(checkActivity(fragment));
        if (factory == null) {
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
        }
        return new ViewModelProvider(ViewModelStores.of(fragment), factory);
    }

    public static ViewModelProvider of(@NonNull FragmentActivity activity,
            @Nullable Factory factory) {
        Application application = checkApplication(activity);
        if (factory == null) {
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
        }
        return new ViewModelProvider(ViewModelStores.of(activity), factory);
    }

我们看到这个方法最终会返回一个ViewModelProvider对象,在生成这个对象的时候需要传递2个参数,第一个参数是ViewModelStore,第二个参数是Factory。第一参数我们等到讨论ViewModel具体是存储在哪边的时候再说,暂且略过。这边先重点看下第二个参数Factory,从代码上来看,如何我们没有显示地传入Factory对象的话,就会去拿一个默认的Factory对象。事实也就是如此,而且这个默认的Factory对象还是个单例。其实,AndroidViewModelFactory就是ViewModelProvider的一个静态内部类,具体代码如下所示:

    public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {

        private static AndroidViewModelFactory sInstance;

        @NonNull
        public static AndroidViewModelFactory getInstance(@NonNull Application application) {
            if (sInstance == null) {
                sInstance = new AndroidViewModelFactory(application);
            }
            return sInstance;
        }

        private Application mApplication;

        public AndroidViewModelFactory(@NonNull Application application) {
            mApplication = application;
        }

        @NonNull
        @Override
        public  T create(@NonNull Class 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);
        }
    }

至此,我们的ViewModelProvider对象总算是创建完成了,接下来就要调用它的get()方法来具体创建我们所需要的ViewModel对象了,具体如下:

    // ViewModelProvider # get(@NonNull Class modelClass)
    public  T get(@NonNull Class 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);
    }

    // ViewModelProvider # get(@NonNull String key, @NonNull Class modelClass)
    public  T get(@NonNull String key, @NonNull Class modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);

        if (modelClass.isInstance(viewModel)) {
            //noinspection unchecked
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }

        viewModel = mFactory.create(modelClass);
        mViewModelStore.put(key, viewModel);
        //noinspection unchecked
        return (T) viewModel;
    }

由上述代码我们可以知道,对于我们想要获取的ViewModel对象,首先会根据key从mViewModelStore中去寻找,看看是否已经创建过,如果找到则直接返回。如果之前没有创建过,则调用mFactory的create()方法去创建,如果我们没有显示地传入Factory对象,则调用默认的Factory对象(即AndroidViewModelFactory单例对象)去创建,由AndroidViewModelFactory源码可知它的create()方法会通过反射去创建我们的ViewModel对象。在创建完毕之后,就会把这个ViewModel对象存入mViewModelStore之中,那么很明显这个mViewModelStore内部肯定是一个HashMap结构。至此,我们已经将ViewModel创建过程解析完毕了,接下来看下创建好的ViewModel是存储在什么地方的。

二、ViewModel存储位置

在解析ViewModel创建过程时,我们遗漏了一个点,那就是创建ViewModelProvider对象时传入的第一个参数ViewModelStore,它是通过ViewModelStores.of(activity)或者ViewModelStores.of(fragment)来创建的。其实,创建出来的ViewModelStore对象最后复制给了我们上面所提到的mViewModelStore,具体可以看下如下源码:

    public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        this.mViewModelStore = store;
    }

而我们通过分析ViewModel创建过程,知道了最终创建出来的ViewModel对象,就是存储在mViewModelStore之中的。那么,现在就很明显了,ViewModel的存储位置其实就是mViewModelStore的存储位置,我们需要搞清楚ViewModelStores.of()方法的执行过程就行。

    public static ViewModelStore of(@NonNull FragmentActivity activity) {
        if (activity instanceof ViewModelStoreOwner) {
            return ((ViewModelStoreOwner) activity).getViewModelStore();
        }
        return holderFragmentFor(activity).getViewModelStore();
    }

    public static ViewModelStore of(@NonNull Fragment fragment) {
        if (fragment instanceof ViewModelStoreOwner) {
            return ((ViewModelStoreOwner) fragment).getViewModelStore();
        }
        return holderFragmentFor(fragment).getViewModelStore();
    }

我们具体看下ViewModelStores.of()方法的执行过程,由于我们的Activity或者Fragment一般不会去实现ViewModelStoreOwner接口,所以基本都是通过holderFragmentFor(xxx).getViewModelStore()这种方式去创建ViewModelStore对象。
那么,我们先来看下这个创建方式的前半段holderFragmentFor(xxx):

    public static HolderFragment holderFragmentFor(FragmentActivity activity) {
        return sHolderFragmentManager.holderFragmentFor(activity);
    }

    public static HolderFragment holderFragmentFor(Fragment fragment) {
        return sHolderFragmentManager.holderFragmentFor(fragment);
    }

很明显,这个方法返回的是一个HolderFragment对象,而且它继承自Fragment。HolderFragment对象的创建都是通过调用sHolderFragmentManager.holderFragmentFor()方法实现的,那么这个sHolderFragmentManager其实是HolderFragment内部类HolderFragmentManager的对象,我们一起看下它的holderFragmentFor()方法,这里以入参为Activity为例:

    HolderFragment holderFragmentFor(FragmentActivity activity) {
            FragmentManager fm = activity.getSupportFragmentManager();
            HolderFragment holder = findHolderFragment(fm);
            if (holder != null) {
                return holder;
            }
            holder = mNotCommittedActivityHolders.get(activity);
            if (holder != null) {
                return holder;
            }

            if (!mActivityCallbacksIsAdded) {
                mActivityCallbacksIsAdded = true;
                activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks);
            }
            holder = createHolderFragment(fm);
            mNotCommittedActivityHolders.put(activity, holder);
            return holder;
    }

    private static HolderFragment findHolderFragment(FragmentManager manager) {
            if (manager.isDestroyed()) {
                throw new IllegalStateException("Can't access ViewModels from onDestroy");
            }

            Fragment fragmentByTag = manager.findFragmentByTag(HOLDER_TAG);
            if (fragmentByTag != null && !(fragmentByTag instanceof HolderFragment)) {
                throw new IllegalStateException("Unexpected "
                        + "fragment instance was returned by HOLDER_TAG");
            }
            return (HolderFragment) fragmentByTag;
        }

        private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
            HolderFragment holder = new HolderFragment();
            fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
            return holder;
        }

    private ActivityLifecycleCallbacks mActivityCallbacks =
                new EmptyActivityLifecycleCallbacks() {
                    @Override
                    public void onActivityDestroyed(Activity activity) {
                        HolderFragment fragment = mNotCommittedActivityHolders.remove(activity);
                        if (fragment != null) {
                            Log.e(LOG_TAG, "Failed to save a ViewModel for " + activity);
                        }
                    }
                };

由源码可知,会先去查找Tag为HOLDER_TAG的HolderFragment是否已经创建过,如果已经创建过则直接返回,从这我们可以得到的信息是一个Activity/Fragment只会创建一个HolderFragment。如果没有找到,则会从mNotCommittedActivityHolders缓存中进行查找,同样如果找到就直接返回。如果仍然没有找到,则说明之前未创建过,则调用createHolderFragment()方法创建HolderFragment对象,并在完成创建之后存入mNotCommittedActivityHolders。同时,我们还注意到,这里还注册了Activity生命周期监听,目的是在Activity销毁的时候可以移除mNotCommittedActivityHolders中的对应数据。
之后,在拿到HolderFragment对象之后,通过调用它的getViewModelStore()方法就可以拿到ViewModelStore对象了,它其实就是HolderFragment的成员变量。

    public ViewModelStore getViewModelStore() {
        return mViewModelStore;
    }

至此,通过以上分析,我们知道通过ViewModelStores.of()方法获取的ViewModelStore对象其实就是HolderFragment的成员变量。那么,也就是说我们创建的ViewModel对象其实也就是保存在所创建出来无UI的HolderFragment之中的。需要注意的一点是,对于同一个Activity/Fragment而言,这种无UI的HolderFragment只会存在一个,但是存在HolderFragment之中的ViewModel可以有多个,即一个Activity/Fragment可以对应多个ViewModel对象。

三、ViewModel旋转屏幕不销毁原理

ViewModel旋转屏幕不销毁的原理其实就在HolderFragment的构造方法之中。

    public HolderFragment() {
        setRetainInstance(true);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mViewModelStore.clear();
    }

    /**
     * Control whether a fragment instance is retained across Activity
     * re-creation (such as from a configuration change).  This can only
     * be used with fragments not in the back stack.  If set, the fragment
     * lifecycle will be slightly different when an activity is recreated:
     * 
    *
  • {@link #onDestroy()} will not be called (but {@link #onDetach()} still * will be, because the fragment is being detached from its current activity). *
  • {@link #onCreate(Bundle)} will not be called since the fragment * is not being re-created. *
  • {@link #onAttach(Activity)} and {@link #onActivityCreated(Bundle)} will * still be called. *
*/ public void setRetainInstance(boolean retain) { mRetainInstance = retain; }

由源码可知,setRetainInstance(boolean) 是Fragment中的一个方法。根据官方注释,我们可以知道,当设置setRetainInstance(true)时,当我们旋转屏幕的时候,Activity会重绘,但是Fragment不会重绘,它将会保留,这也就意味着旋转屏幕时Fragment的onCreate和onDestory方法都不会调用。
无UI的HolderFragment正是运用了这个特性,在其中存放一个专门用于存储ViewModel对象的ViewModelStore(其内部为HashMap),如此ViewModelStore中所有的ViewModel都不会因为屏幕旋转等配置变化后而销毁。

总结

在创建ViewModel对象的过程中,会生成一个无UI的HolderFragment。对于同一个Activity/Fragment,只会有一个HolderFragment,但是可以有多个ViewModel。ViewModel存储在无UI的HolderFragment中,具体以键值对形式存储在ViewModelStore的HashMap中。Activity/Fragment的HolderFragment会保存在全局单例的HolderFragmentManager的HashMap中,在Activity/Fragment销毁时会进行相应的移除。

你可能感兴趣的:(Android架构组件之ViewModel源码解析)