ViewModel源码阅读

374 阅读3分钟

ViewModel用来管理Activity或Fragment中的数据, 当Activity配置发生改变时数据并不会丢失。 ViewModel 在Activity 重建时,数据会先保存在ComponentActivity 中的NonConfigurationInstances中,NonConfigurationInstances定义如下 image.png viewModel保存在NonConfigurationInstances的成员变量mMap中 ,mMap是一个HashMap, 以 DEFAULT_KEY+ViewModel的类名 作为Key ,ViewModel作为Value image.png

ViewModel的保存

Activity#retainNonConfigurationInstances方法,用于获取要保存的NonConfigurationInstances

    NonConfigurationInstances retainNonConfigurationInstances() {
        
        Object activity = onRetainNonConfigurationInstance();
      
        HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
       
        HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
        FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();


        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.activity = activity;

        return nci;
    }

Fragment的保存与恢复也在这里,Fragment的保存与恢复本文就不做深究了 ,只看Viewmodel相关的 ,跟进 onRetainNonConfigurationInstance image.png onRetainNonConfigurationInstance方法可以看到把封装好的viewModelStore返回了。
总结一下:Activity和ComponentActivity 都有一个的同名的内部类NonConfigurationInstances ,Activity持有了ComponentActivity的NonConfigurationInstances , viewModelStore保存在ComponentActivity的NonConfigurationInstances中 。
那么Activity#retainNonConfigurationInstances方法又是在哪里调用的?

在ActivityThread中搜索retainNonConfigurationInstancesimage.png
可以看到只有一处搜索结果 ,在performDestroyActivity方法中被调用 。并将数据保存在r.activity中,r为ActivityClientRecord ,从mActivities中获取,mActivities是一个Map,value为ActivityClientRecord 。
那么ViewModel保存的时间又是多久?继续查看performDestroyActivity方法

    ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
            int configChanges, boolean getNonConfigInstance, String reason) {
        ActivityClientRecord r = mActivities.get(token);
        
        if (r != null) {

            if (getNonConfigInstance) {
                try {
                    r.lastNonConfigurationInstances
                            = r.activity.retainNonConfigurationInstances();
                } 
            }
            
        synchronized (mResourcesManager) {
            mActivities.remove(token);
        }
        return r;
    }
    }

在performDestroyActivity方法末尾可以看到mActivities又会把当前的ActivityClientRecord移除然后返回,初步可以推断viewmodel保存的时间应该不会很久。 performDestroyActivity方法在ActivityThread中的调用链:
handleRelaunchActivity -> handleRelaunchActivityInner ->handleDestroyActivity -> performDestroyActivity 只要知道ActivityThread# handleRelaunchActivity 在什么地方调用就能知道答案了。

我们知道Activity在配置发生改变后,Viewmodel的数据并不会发生改变。Activity横竖屏切换可以模拟这个场景,我们看下Activity横竖屏切换做了些什么。Activity有个setRequestedOrientation方法,可以切换横竖屏。

    public void setRequestedOrientation(@ActivityInfo.ScreenOrientation int requestedOrientation) {
        if (mParent == null) {
            try {
                ActivityTaskManager.getService().setRequestedOrientation(
                        mToken, requestedOrientation);
            } catch (RemoteException e) {
                // Empty
            }
        }
    }

image.png 获取的是ATMS在client 端的Binder代理对象。通过Binder最终会调到ATMS的setRequestedOrientation方法。

ATMS的setRequestedOrientation方法

image.png ActivityRecord和ActivityClientRecord 是一对一的关系, 一个在应用进程一个在服务进程,跟进r.setRequestedOrientation(requestedOrientation);

    void setRequestedOrientation(int requestedOrientation) {
        setOrientation(requestedOrientation, mayFreezeScreenLocked(app));
        mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
                task.taskId, requestedOrientation);
    }

最终会调用ensureActivityConfiguration。

    boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
            boolean ignoreVisibility) {
        final ActivityStack stack = getActivityStack();

        final int changes = getConfigurationChanges(mTmpConfig);
        // Update last reported values.
        final Configuration newMergedOverrideConfig = getMergedOverrideConfiguration();

        if (shouldRelaunchLocked(changes, mTmpConfig) || forceNewConfig) {   
             
            if (!attachedToProcess()) {

            }  else if (mState == RESUMED) {

                relaunchActivityLocked(true /* andResume */, preserveWindow);
            } else {
        
                relaunchActivityLocked(false /* andResume */, preserveWindow);
            }

            return false;
        }

        return true;
    }

又会根据是否要执行resume 来调用relaunchActivityLocked方法,此处情况resume = true。

    void relaunchActivityLocked(boolean andResume, boolean preserveWindow) {

        try {
      
            mStackSupervisor.activityRelaunchingLocked(this);
            final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults,
                    pendingNewIntents, configChangeFlags,
                    new MergedConfiguration(mAtmService.getGlobalConfiguration(),
                            getMergedOverrideConfiguration()),
                    preserveWindow);
            final ActivityLifecycleItem lifecycleItem;
            if (andResume) {
                lifecycleItem = ResumeActivityItem.obtain(
                        getDisplay().mDisplayContent.isNextTransitionForward());
            } else {
                lifecycleItem = PauseActivityItem.obtain();
            }
            final ClientTransaction transaction = ClientTransaction.obtain(app.getThread(), appToken);
            transaction.addCallback(callbackItem);
            transaction.setLifecycleStateRequest(lifecycleItem);
            mAtmService.getLifecycleManager().scheduleTransaction(transaction);
   
        }

    }

最终会调用ActivityRelaunchItem #execute , 我们看看ActivityRelaunchItem#execute

    @Override
    public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        client.handleRelaunchActivity(mActivityClientRecord, pendingActions);
    }

client就是ActivitThread , 接着就调用我们开头的handleRelaunchActivity方法了 。

总结 : ViewModel的数据保存在ActivityClientRecord 中,ActivityClientRecord 又是保存在ActivityThrad 的mActivities中 , 并且ViewModel保存只是暂时的,在Activity重建时从mActivities中移除。

ViewModel的恢复

    private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
            List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
            PendingTransactionActions pendingActions, boolean startsNotResumed,
            Configuration overrideConfig, String reason) {

        //保存viewmodel的数据到ActivityClientRecord中
        handleDestroyActivity(r.token, false, configChanges, true, reason);

        
        //重建Activity
        handleLaunchActivity(r, pendingActions, customIntent);
    }

在handleDestroyActivity 保存viewmodel数据后 , 接着调用handleLaunchActivity重建activity,省略后的代码如下。

    public Activity handleLaunchActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, Intent customIntent) {

        final Activity a = performLaunchActivity(r, customIntent);

        return a;
    }


    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
 
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            
            //构建新的Activity
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
  
        } 
        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);

            if (activity != null) {
                
                //恢复Viewmodel数据
                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,
                        r.assistToken);

                //置空
                r.lastNonConfigurationInstances = null;
             
            }
            r.setState(ON_CREATE);

            synchronized (mResourcesManager) {
                mActivities.put(r.token, r);
            }

        } 
        return activity;
    }

Activity.attach方法中将刚刚保存在ActivityClientRecord的NonConfigurationInstances恢复,viewmodel存在NonConfigurationInstances中也就恢复了。