ViewModel源码分析

ViewModel是什么?ViewModel是负责管理Activity和Fragment的数据的类,他独立于Activity和Fragment之外,即使Activity和Fragment因为屏幕旋转等原因销毁,只要Activity与Fragment重新创建,即可与原先的ViewModel重新绑定。首先查看ViewModel的获取方式:
val viewModel = ViewModelProviders.of(target, factory).get(PlanViewModel::class.java)
ViewModelProviders.of()方法获取ViewModelProvider的实例,然后通过get()方法获取ViewModel,ViewModelProvider.get()实现如下:

public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> 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;
    }

ViewModelProvider.get(key, class)方法,先查询要获取的viewmodel实例是否已经在ViewModelStroe中,如果实例已经存在于ViewModelStore中,则返回之前的实例,否则通过ViewModelProvider.Factory创建一个viewModel实例,并存放于viewModel中,下次再获取该类型的viewModel时,只要是同一个ViewModelStroe,就可以将缓存的viewModel返回。那么如何保证即使Activity重新创建后,获取到viewModel还是之前的ViewModel呢?由于ViewModelStroe内部仅仅是通过Map去管理ViewModel的缓存,所以只有保证Activity销毁重建后,ViewModelStroe是同一个,即可保证拿到的是原来的ViewModel。ViewModelStroe是从哪里来的呢?
继续看ViewModelProviders.of()的实现:

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(fragment.getViewModelStore(), 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(activity.getViewModelStore(), factory);
    }

通过of方法的实现可以看到ViewModelStore由实现了ViewModelStoreOwner的Activity和Fragment提供,下面分2部分分别去查看Activity和Fragment的实现;

Activity是如何管理ViewModelStore的

Activity都重新创建了,activity是如何保证ViewModelStore还是原来的呢?
下面看Activity.getViewModelStroe的实现:

ComponentActivity.java
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;
    }

Activity通过NonConfigurationInstances 实现对ViewModelStore的管理,显示获取最后的NonConfigurationInstances 实例,然后将NonConfigurationInstances.viewModelStore赋值给自己,这个NonConfigurationInstances.viewModelStore就是Activity销毁重建前的ViewModelStore

public Object getLastNonConfigurationInstance() {
        return mLastNonConfigurationInstances != null
                ? mLastNonConfigurationInstances.activity : null;
    }

到这里又看到一个NonConfigurationInstances,这里有点迷惑性,getViewModelStore里的NonConfigurationInstances是ComponentActivity.NonConfigurationInstances,getLastNonConfigurationInstance里的是Activity.NonConfigurationInstances,ComponentActivity.NonConfigurationInstances管理ViewModelStore,而Activity.NonConfigurationInstances则管理ComponentActivity.NonConfigurationInstances。接下来继续看getLastNonConfigurationInstance(),这个mLastNonConfigurationInstances 是在哪里创建的呢?是在activity.attach()里作为参数从外界传入的。activity.attch()则是在ActivityThread.performLaunchActivity()里被调用。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ...
        activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);
        ...
        return activity;
}

继续追踪到ActivityThread.performLaunchActivity,可以看到,Activity.lastNonConfigurationInstances来源于ActivityClientRecord.lastNonConfigurationInstances,那么ActivityClientRecord.lastNonConfigurationInstances是什么时候被赋值的呢?答案是在activity销毁的时候,我们继续看ActivityThread.performDestroyActivity():

ActivityThread.performDestroyActivity() {
    if (getNonConfigInstance) {
                try {
                    r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();
                } catch (Exception e) {
                    if (!mInstrumentation.onException(r.activity, e)) {
                        throw new RuntimeException(
                                "Unable to retain activity "
                                + r.intent.getComponent().toShortString()
                                + ": " + e.toString(), e);
                    }
                }
            }
}

在ActivityThread.performDestroyActivity() 中将Activity.retainNonConfigurationInstances()返回值赋给ActivityClientRecord.lastNonConfigurationInstances,继续看Activity.retainNonConfigurationInstances():

NonConfigurationInstances retainNonConfigurationInstances() {
        Object activity = onRetainNonConfigurationInstance();
        HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
        //获取Fragment的mNonConfig快照信息,用于重新恢复Fragment
        FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();

        // We're already stopped but we've been asked to retain.
        // Our fragments are taken care of but we need to mark the loaders for retention.
        // In order to do this correctly we need to restart the loaders first before
        // handing them off to the next activity.
        mFragments.doLoaderStart();
        mFragments.doLoaderStop(true);
        ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();

        if (activity == null && children == null && fragments == null && loaders == null
                && mVoiceInteractor == null) {
            return null;
        }

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.activity = activity;
        nci.children = children;
        nci.fragments = fragments;
        nci.loaders = loaders;
        if (mVoiceInteractor != null) {
            mVoiceInteractor.retainInstance();
            nci.voiceInteractor = mVoiceInteractor;
        }
        return nci;
    }

在retainNonConfigurationInstances()中,会创建返回一个Activity.NonConfigurationInstance实例,并且在里边看到熟悉的代码:
Object activity = onRetainNonConfigurationInstance();
nci.activity = activity;
再回到getLastNonConfigurationInstance()方法中,
return mLastNonConfigurationInstances != null ? mLastNonConfigurationInstances.activity : null;
由此我们知道onRetainNonConfigurationInstance()返回的是管理ViewModelStore的ComponentActivity.NonConfigurationInstance对象,接着看其实现:

@Override
    @Nullable
    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()创建了一个持有ViewModelStroe的ComponentActivity.NonConfigurationInstance对象,到此我们追踪了一遍Activity销毁重新创建,而ViewModel保持不变的流程。总结下:
1、Activity销毁时,通过Activity.retainNonConfigurationInstances()创建Activity.NonConfigurationInstace对象交给ActivityClientRcord保存,而Activity.NoConfigurationInstance.activity持有ComponentActivity.NonConfigurationInstance对象,ComponentActivity.NonConfigurationInstance则持有了ViewModelStore
2、Activity创建后,在Activity.attch()中,将ActivityClientRcord持有的Activity.NonConfigurationInstace对象赋值给Activity.mLastNonConfigurationInstances
3、当调用ViewModelProviders.of()的时候,会调用ComponentActivity.getViewModelStore(),在getViewModelStore()中,会通过2的mLastNonConfigurationInstances获取到ComponentActivity.NonConfigurationInstance对象,并将ComponentActivity.NonConfigurationInstance持有的ViewModelStore返回。
这样就保证了Activity销毁重新创建,而ViewModelStore还是那个销毁前的ViewModelStore,所以我们通过
ViewModelProviders.of(target, factory).get(ViewModel.class)获取到的也还是Activity销毁前的那个ViewModel.

Fragment如何管理ViewModelStore

Fragment.getViewModelStore()实现如下:

public ViewModelStore getViewModelStore() {
       if (mFragmentManager == null) {
           throw new IllegalStateException("Can't access ViewModels from detached fragment");
       }
       return mFragmentManager.getViewModelStore(this);
   }

fragment自己并不直接管理ViewModel,通过代理FragmentManager.getViewModel()返回ViewModel,接下去再看FragmentManger.getViewModel的实现:FragmentManagerImpl.getViewModel()

ViewModelStore getViewModelStore(@NonNull Fragment f) {
        return mNonConfig.getViewModelStore(f);
    }

FragmentManagerImpl又通过mNonConfig去管理ViewModelStore,我们看看mNonConfig时在什么时候创建的:

public void attachController(@NonNull FragmentHostCallback host,
                                 @NonNull FragmentContainer container, @Nullable Fragment parent) {
        if (mHost != null) throw new IllegalStateException("Already attached");
        mHost = host;
        mContainer = container;
        mParent = parent;
        if (parent != null) {
            mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
        } else if (host instanceof ViewModelStoreOwner) {
            ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
            mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
        } else {
            mNonConfig = new FragmentManagerViewModel(false);
        }
    }

根据Fragment是否是子Fragment,以及Activity是否实现了ViewModelStoreOwner分为3中情况:
1、Fragment根Fragment且Activity实现了ViewModelStoreOwner接口时,从Activity.getViewModelStore获取viewModelStore,根据我们上面的分析可以知道这个viewModelStore是销毁重新创建前的viewModelStore,由于mNonConfig是个ViewModel,所以通过viewModelStore获取到的mNonConfig也是销毁重建前的mNonCofig。
2、fragment是子fragment时,mNonConfig从父Fragment的mNonConfig中获取,如果activity实现了ViewModelStoreOwner,则回到1,mNonCofig还是销毁前的那个mNonCofig;如果activity没有实现ViewModelStoreOwner,则回到3.
3.如果Activity没有实现ViewModelStoreOwner时,则从新创建一个全新的mNonConfig.
由于mNonConfig是销毁之前的mNonConfig,则通过mNonConfig管理的ViewModelStore也自然是销毁前的ViewModelStore。由此可见保证fragment的viewModeStore时销毁之前的ViewModelStore,是依赖于Activity的实现的。

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

推荐阅读更多精彩内容