【大揭秘】Android架构组件ViewModel来龙去脉

ViewModel是google官方的MVVM架构组件,目前已经集成到了最新的支持库中了,是MVVM架构的核心组件之一。不懂MVVM的请看之前的文章:(一)Android官方MVVM框架实现组件化之整体结构

1.ViewModel官方解释

网上看到的ViewModel的博文千篇一律,实在忍不了,自己写看了源码写了一篇,欢迎拍砖!

ViewModel是存储UI相关数据并不会因为旋转而销毁的类。

最为重要的就是ViewModel具有下面的生命周期,这就是ViewModel的最可贵之处:


2.ViewModel生命周期,Activity配置变化ViewModel也不会销毁

正因为ViewModel有如此的生命周期,所以ViewModel在MVVM可以作为数据存储区,是连接View和Model重要组件,ViewModel的核心作用如下图所示:


ViewModel在MVVM中的核心作用

这篇文字要弄清楚下面几个问题:

  • 1.ViewModel是怎么创建的?
  • 2.ViewModel是怎么存储的?
  • 3.ViewModel为什么可以实现旋转屏幕不销毁?

先放简单讲一下ViewModel的基本使用方法,我们在获取ViewModel的时候绝对不能直接使用new关键字去创建,需要使用 ViewModelProviders 去使用系统提供的反射方法去创建我们想要的ViewModel,下面是官方架构组件android.arch.lifecycle包下面的ViewModelProviders工具类用来获取ViewModel:

/**
 * 注解by danxx on 2018/3/31.
 * Global ViewModel Provider
 * ViewModel的创建不可直接new,需要使用这个{@link ViewModelProviders}才能与Activity或者
 * Fragment的生命周期关联起来!
 */
public class ViewModelProviders {

    /**
     * 通过Activity获取可用的Application
     * 或者检测Activity是否可用
     * @param activity
     * @return
     */
    private static Application checkApplication(Activity activity) {
        Application application = activity.getApplication();
        if (application == null) {
            throw new IllegalStateException("Your activity/fragment is not yet attached to "
                    + "Application. You can't request ViewModel before onCreate call.");
        }
        return application;
    }

    /**
     * 通过Fragment获取Activity
     * 或者检测Fragment是否可用
     * @param fragment
     * @return
     */
    private static Activity checkActivity(Fragment fragment) {
        Activity activity = fragment.getActivity();
        if (activity == null) {
            throw new IllegalStateException("Can't create ViewModelProvider for detached fragment");
        }
        return activity;
    }

    /**
     * 通过Fragment获得ViewModelProvider
     * @param fragment
     * @return
     */
    @NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull Fragment fragment) {

        /**获取默认的单例AndroidViewModelFactory,它内部是通过反射来创建具体的ViewModel*/
        ViewModelProvider.AndroidViewModelFactory factory =
                ViewModelProvider.AndroidViewModelFactory.getInstance(
                        checkApplication(checkActivity(fragment)));
        /***
         *   利用HolderFragment来关联生命周期并使用HolderFragment中的ViewModelStore的HashMap存储ViewModel
         *   AndroidViewModelFactory创建ViewModel
         */
        return new ViewModelProvider(ViewModelStores.of(fragment), factory);
    }

    /**
     * 通过FragmentActivity获得ViewModelProvider
     * @param activity
     * @return
     */
    @NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull FragmentActivity activity) {
        /**获取默认的单例AndroidViewModelFactory,它内部是通过反射来创建具体的ViewModel*/
        ViewModelProvider.AndroidViewModelFactory factory =
                ViewModelProvider.AndroidViewModelFactory.getInstance(
                        checkApplication(activity));
        /***
         *   利用HolderFragment来关联生命周期并使用HolderFragment中的ViewModelStore的HashMap存储ViewModel
         *   AndroidViewModelFactory创建ViewModel
         */
        return new ViewModelProvider(ViewModelStores.of(activity), factory);
    }

    /**
     *
     * @param fragment
     * @param factory 提供了自定义创建ViewModel的方法
     * @return
     */
    @NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull Fragment fragment, @NonNull ViewModelProvider.Factory factory) {
        //检测Fragment 
        checkApplication(checkActivity(fragment));
        return new ViewModelProvider(ViewModelStores.of(fragment), factory);
    }

    /**
     *
     * @param activity
     * @param factory 提供了自定义创建ViewModel的方法
     * @return
     */
    @NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull FragmentActivity activity,
                                       @NonNull ViewModelProvider.Factory factory) {
        //检测activity
        checkApplication(activity);
        return new ViewModelProvider(ViewModelStores.of(activity), factory);
    }
}


有一些解释我已经放在注释里了,有兴趣的可以仔细看看。

开始创建使用ViewModel了:

1.在Activity中创建使用ViewModel:

        /**转入Activity就行*/
        GirlsViewModel girlsViewModel =
        ViewModelProviders.of(ActivityGirls.this).get(GirlsViewModel.class);

2.在Fragment中创建使用ViewModel:

       /**转入Fragment就行*/
       GirlsViewModel girlsViewModel =
       ViewModelProviders.of(FragmentGirls.this).get(GirlsViewModel.class);

3.在任意地方创建使用ViewModel:

      /**将context强转成FragmentActivity就行*/
      GirlsViewModel girlsViewModel =
      ViewModelProviders.of((FragmentActivity) context).get(GirlsViewModel.class);

ViewModel 的存在是依赖 Activity 或者 Fragment的,不管你在什么地方获取ViewModel ,只要你用的是相同的Activity 或者 Fragment,那么获取到的ViewModel将是同一个 (前提是key值是一样的),所以ViewModel 也具有数据共享的作用!

一、ViewModel是怎么创建的?

上面创建ViewModel链式调用分解为下面两步:

        /*****第一步:根据Activity或者Fragment获得ViewModelProvider****/
        ViewModelProvider viewModelProvider = ViewModelProviders.of(ActivityGirls.this);

        /*****第二步:使用ViewModelProvider反射创建需要的ViewModel****/
        GirlsViewModel girlsViewModel = viewModelProvider.get(GirlsViewModel.class);

先看第一步获得的源代码:

    public static ViewModelProvider of(@NonNull FragmentActivity activity) {

        /**********获得AndroidViewModelFactory ( 内部是单例的 )*******/
        ViewModelProvider.AndroidViewModelFactory factory =
                ViewModelProvider.AndroidViewModelFactory.getInstance(
                        checkApplication(activity));

        /*****创建一个ViewModelProvider( 传入的两个参数是重点 )*****/
        return new ViewModelProvider(ViewModelStores.of(activity), factory);

    }

上面的两步其实很关键了,获得AndroidViewModelFactory ,AndroidViewModelFactory其实是ViewModelProvider的静态内部类,看调用方式就知道是一个单例的,就是我们的应用只有有一个单例的 AndroidViewModelFactory存在,看源码:

  /*****`AndroidViewModelFactory `其实是`ViewModelProvider`的静态内部类******/
 public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {

        private static AndroidViewModelFactory sInstance;

        /********获得AndroidViewModelFactory 单例**********/
        public static AndroidViewModelFactory getInstance(@NonNull Application application) {
            if (sInstance == null) {
                sInstance = new AndroidViewModelFactory(application);
            }
            return sInstance;
        }

        private Application mApplication;
        
        /*************其实构造方式是public 的,还是可以new的****************/
        public AndroidViewModelFactory(@NonNull Application application) {
            mApplication = application;
        }
        
        /******其实这里就是创建ViewModel的关键地方,根据给出的Class反射创建需要的ViewModel*******/
        @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);
        }
    }

重点我都标注在注释里了,请耐心看一遍。看到这里我知道了一个全局的AndroidViewModelFactory工具类,作用就是反射创建我们想要的类ViewModel,其实功能简单的!

获得到的单例AndroidViewModelFactory是创建ViewModelProvider的第二个参数,下面我们看第一个参数。

第一个参数是这样: ViewModelStores.of(activity)
看源码:

3.ViewModelStores静态方法为Activity创建一个ViewModelStore

解释我就放在注释了,大家看下面的注释把:

    @MainThread
    public static ViewModelStore of(@NonNull FragmentActivity activity) {
        //如果你的Activity实现了ViewModelStoreOwner接口具备了提供
        //ViewModelStore 的功能就直接获取返回,通常我们的Activity都不会去实现这个功能
        if (activity instanceof ViewModelStoreOwner) {
            return ((ViewModelStoreOwner) activity).getViewModelStore();
        }
        //系统为你的Activity添加一个具有提供ViewModelStore 的holderFragment
        return holderFragmentFor(activity).getViewModelStore();
    }

其实解析ViewModelStore就可以解释ViewModel的存储,解析 holderFragmentFor(activity).getViewModelStore() 就可解释ViewModel为什么可以在Activity配置发生变化的情况下人不销毁,这些我们就在下面去解释。我第一步重点解释创建不关心其他的。

到这里我们要知道:
第一: AndroidViewModelFactory在正常情况下是全局单例只有一个,只是一个反射创建对象的工具类。
第二:ViewModelProvider是每次获取创建ViewModel的时候都会创建一个新的。
第三:ViewModelStore是每一个Activity或者Fragment都有一个。

关注ViewModel创建:

//viewModelProvider的get方法
 viewModelProvider.get(GirlsViewModel.class);
4.viewModelProvider的get方法

会用 DEFAULT_KEY 和 类名组成一个key值去获取,接着向下看:


5.ViewModel的创建关键方法

代码很简单,流程如下:
(1) 先从mViewModelStore中使用key去获取ViewModel, mViewModelStore中是使用HashMap去存储一个Activity或者FragmentViewModel的。如果获取到就返回。
(2) 没获取到就使用单例mFactory的create方法反射创建ViewModel,create方法的代码在上面贴出来了。
(3) 使用Key存入mViewModelStore 并返回。

到这里ViewModel的创建基本就是讲完了,但是可能还是有些懵逼。下面接着看吧。

一句话总结ViewModel是怎么被创建的:

创建一个ViewModelProvider,使用ViewModelProvider内部的全局单例AndroidViewModelFactory来反射创建 ViewModel,并把创建的ViewModel存入传入的ViewModelStore中!

二、ViewModel是怎么存储的?

存储就是要讲解ViewModelStore了。
直接看源代码:

6.ViewModelStore源码

代码就这数的清的几行,就是一个 HashMap用存储ViewModel。提供get,put,clear三个方法。
上面说了ViewModelStore是每一个Activity或者ViewModel都有一个的,当Activity或者Fragment销毁的时候就会调用clear方法了。

ViewModelStore被谁创建,被谁持有?-------------------> +_+
抢答:被HolderFragment创建和持有!

HolderFragment跟我们的Activity或者Fragment有什么关系?-------------------> +_+
抢答:当我们要给Activity或者Fragment创建ViewModel的时候,系统就会为Activity或者Fragment添加一个HolderFragmentHolderFragment中会创建持有一个ViewModelStore

HolderFragment怎么创建怎么被添加?-------------------> +_+

7.每次获取ViewModel都会创建一个ViewModelProvider

这一步其实可以分解为下面的的样子:

        /**为Activity或者Fragment创建ViewModelStore*/
        ViewModelStore viewModelStore = ViewModelStores.of(activity);

        /**为本次的ViewModel获取创建一个ViewModelProvider*/
        ViewModelProvider viewModelProvider =  new ViewModelProvider(viewModelStore, factory);

完成了上面两步才可以这样: viewModelProvider.get(想要的ViewModel.class);

正是

       /**为Activity或者Fragment创建ViewModelStore*/
       ViewModelStore viewModelStore = ViewModelStores.of(activity / fragment);

这步为我们的 activity / fragment 注入了一个HolderFragment,创建HolderFragment的时候会创建的时候会创建一个ViewModelStore实例,到这里也解释了一下 ViewModelStore被谁创建,被谁持有?的问题。

上面我有提过ViewModelStoreOwner这个接口,其实我们这里说的HolderFragment就是实现了这个接口的Fragment。

一句话总结ViewModel是怎么被存储的:

这是上面一句话总结ViewModel的创建:

8.ViewModel创建一句话总结

这句创建总结其实也说明了ViewModel的存储。
进一步解释:
ViewModel是存储在当前Activity / FragmentHolderFragment 中的ViewModelStore的HashMap中,我们可以get,put或者在Activity / Fragment销毁的时候HolderFragment会跟随销毁,在HolderFragmentonDestroy方法中调用mViewModelStoreclear方法。
9.HolderFragment伴随销毁时调用自己所有ViewModel的onCleared方法

三、ViewModel为什么可以实现旋转屏幕不销毁?

ViewModel的创建获取方式是: 为Activity / Fragment创建一个ViewModelStore,获取到AndroidViewModelFactory单例,用这个两个数据创建一个ViewModelProvider,在创建的ViewModelProvider中可以get我们要的ViewModel。

Activity / Fragment创建一个ViewModelStore,就是调用下面的方法:

10.HolderFragment被注入到Activity / Fragment

holderFragmentFor(activity)源码:
11.holderFragmentFor(activity)

12.单例的HolderFragmentManager

holderFragmentFor(activity)方法每一步都有解释,很详细:


        HolderFragment holderFragmentFor(FragmentActivity activity) {

            //获取Activity的FragmentManager
            FragmentManager fm = activity.getSupportFragmentManager();

            //通过HOLDER_TAG在FragmentManager中需要HolderFragment
            HolderFragment holder = findHolderFragment(fm);

            //获得的HolderFragment不为空就返回
            if (holder != null) {
                return holder;
            }

            //在Map<Activity, HolderFragment>缓存中获取HolderFragment
            //Activity为key,所以每一个Activity或者Fragment只会有一个HolderFragment
            holder = mNotCommittedActivityHolders.get(activity);

            //不为空就返回
            if (holder != null) {
                return holder;
            }


            //在Application中注册一个所有Activity生命周期回调监听,这里只会注册一次
            //这里注册Activity生命周期监听的目的是在Activity销毁的时候好移除Map<Activity, HolderFragment>中的对应数据
            if (!mActivityCallbacksIsAdded) {
                mActivityCallbacksIsAdded = true;
                activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks);
            }

            //new HolderFragment()并通过fm添加到Activity并返回
            holder = createHolderFragment(fm);

            //添加到Map<Activity, HolderFragment>缓存
            mNotCommittedActivityHolders.put(activity, holder);

            //返回
            return holder;
        }

createHolderFragment()方法重点关注一下:

12.createHolderFragment

就是创建了一个HolderFragment,使用传入的FragmentManager添加进去!

这是上面调用的构造方法:


13.HolderFragment

被设置setRetainInstance(true)后的Fragment添加到Activity中去了,会怎么样?

setRetainInstance(boolean) 是Fragment中的一个方法。将这个方法设置为true就可以使当前Fragment在Activity重建时存活下来, 如果不设置或者设置为 false, 当前 Fragment 会在 Activity 重建时同样发生重建, 以至于被新建的对象所替代。
在setRetainInstance(boolean)为true的 Fragment (就是HolderFragment)中放一个专门用于存储ViewModel的Map, 这样Map中所有的ViewModel都会幸免于Activity的配置改变导致的重建,让需要创建ViewModel的Activity, Fragment都绑定一个这样的Fragment(就是HolderFragment), 将ViewModel存放到这个 Fragment 的 Map 中, ViewModel 组件就这样实现了。

实现原理就是巧妙滴借用了Fragment的setRetainInstance(true)属性。关于setRetainInstance更多介绍可以参考:Android应用开发:Fragment的非中断保存setRetaineInstance

我这里可以展示一下setRetainInstance(true)属性对生命周期的影响,在一个Activity中加入一个具有setRetainInstance(true)属性的Fragment:


setRetainInstance(true)属性演示

三秒钟后展示出了我们的Fragment,生命周期如下:


展示生命周期

然后我们点击模拟器的旋转屏幕按钮:


旋转屏幕按钮

生命周期变化:
旋转生命周期

可以看到Activity因为配置改变了,调了onDestroy方法,但是我们的setRetainInstance(true)属性的Fragment没有调用onDestroy方法,说明Fragment得以幸存下来了。

退出当前Activity但是不退出应用后的生命周期:

退出Activity后生命周期

这时候Activity和Fragment的onDestroy生命周期方法先后被调用了。

在我的HolderFragmentonDestroy方法中,会调用mViewModelStore中所有ViewModelonCleared方法。

四、总结:

关于ViewModel的实现结构图可以参考如下:图片来源https://blog.csdn.net/zhuzp_blog/article/details/78910535

13.ViewModel的实现结构图

1.ViewModel 以键值对的形式存在Activity或者Fragment的HolderFragment
ViewModelStore的HashMap中。

2.一个Activity或者Fragment可以有很多个ViewModel

3.一个Activity或者Fragment只会有一个HolderFragment

4.Activity或者Fragment的HolderFragment会保存在全局单例的HolderFragmentManager的HashMap中,在Activity或者Fragment销毁的时候会移除HashMap中对应的value。

5.因为ViewModel是以Activity或者Fragment为存在基础,所以ViewModel可以在当前Activity和Fragment中实现数据共享,前提是传入相同的key值。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容