ViewModel是如何在Activity发生旋转时保留数据的?

3,046

版本

'androidx.appcompat:appcompat:1.2.0'
想要ViewModel不随着宿主重建而销毁,那就要保证ViewModelStore不随着宿主重建而销毁。那么ViewModelStore又是在什么时机被保存起来的呢?

答: 通过ComponentActivity中的onRetainNonConfigurationInstance和getLastNonConfigurationInstance方法保存ViewModelStore对象

1.首先看一下ComponentActivity中获取ViewModelStore的getViewModelStore()方法

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,如果不为空再获取viewModelStore
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            //如果未获取到,则创建ViewModelStore对象
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }
NonConfigurationInstances
static final class NonConfigurationInstances {
        Object custom;
        ViewModelStore viewModelStore;
    }

2.接下来看ComponentActivity中onRetainNonConfigurationInstance()方法如何保存viewModelStore

public final Object onRetainNonConfigurationInstance() {
        Object custom = onRetainCustomNonConfigurationInstance();

        ViewModelStore viewModelStore = mViewModelStore;
        
        if (viewModelStore == null) {
            // 如果NonConfigurationInstance保存了viewModelStore,把它取出来
            
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                viewModelStore = nc.viewModelStore;
            }
        }

        if (viewModelStore == null && custom == null) {
            return null;
        }

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        
        // 把viewModelStore放到NonConfigurationInstances中并返回,这样当页面被重建而销毁时ViewModelStore就被保存起来了
        nci.viewModelStore = viewModelStore;
        return nci;
    }
public Object getLastNonConfigurationInstance() {
        return mLastNonConfigurationInstances != null
                ? mLastNonConfigurationInstances.activity : null;
    }

3.那么onRetainNonConfigurationInstance()方法又是何时被调用的呢?

在Activity启动流程中,当ActivityThread执行performDestroyActivity这个方法时,会调用Activity的retainNonConfigurationInstances()方法获取到保存的数据并保存到ActivityClientRecord中。

//Activity中的retainNonConfigurationInstances()方法
NonConfigurationInstances retainNonConfigurationInstances() {
        //ComponentActivity中的onRetainNonConfigurationInstance()方法
        Object activity = onRetainNonConfigurationInstance();
        ···
        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.activity = activity;
        ···
        return nci;
    }
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing, int configChanges, boolean getNonConfigInstance, String reason) {
   ...
   //保存retainNonConfigurationInstances中的数据到ActivityClientRecord中
   ActivityClientRecord r = mActivities.get(token);
   r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();
   ...
   return r;

当页面重建完成,ActivityThread执行了performLaunchActivity方法时,会调用Activity的attach方法,便会把刚刚存储的数据,传递进去。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    //传递之前储存的数据
    activity.attach(......, r.lastNonConfigurationInstances,.....);
  }