In view of the ViewModel source analysis, this article from the Factory starting point, step by step analysis of the creation and recovery of ViewModel.

The creation of the ViewModel

First, let’s look at the constructor of ViewModelProivder. ViewModelProivder has many constructors, but they all end up in the same place:

public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
        this(owner.getViewModelStore(), factory);
    }

ViewModelProvider(this.object : ViewModelProvider.Factory {
           override fun <T : ViewModel> create(modelClass: Class<T>) =
                MyViewModel() as T
        })
Copy the code

MFactory is the factory class we expect to create ViewModel objects; What is mViewModelStore? MViewModelStore is used to store ViewModel objects. For example, the onCreate method of the same Activity may call multiple callbacks, so we initialize the ViewModel in onCreate, But there’s no way that every onCreate callback is going to create a new ViewModel object, so you need something to store the ViewModel that we created before, and that’s what the ViewModelStore does.

ViewModelStoreOwner The interface for ComponentActivity and Fragment is implemented in the class diagram.

Let’s look at the get method again, because that’s how you actually get the ViewModel object.

    public <T extends ViewModel> T get(@NonNull Class<T> 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);
    }
 
Copy the code

This get method does nothing but construct a default key and then call another GET method. Let’s take a look:

    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);
 
        if (modelClass.isInstance(viewModel)) {
            if (mFactory instanceof OnRequeryFactory) {
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if(viewModel ! =null) {
                // TODO: log a warning.}}if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }
 
Copy the code

In general, this GET method is mainly divided into the following two processes:

Retrieves the ViewModel object from the ViewModelStore(cache) by key, and returns it if it exists in the cache. After the Activity is reconstructed in landscape, the object L that returns ViewMode is returned here. If the cache does not exist, an object is created via Factory, placed in the cache, and returned.

The recovery of the ViewModel

We all know that the ViewModel is retrieved from a ViewModelStore cache. We looked at the ViewModelStore source code and found that its internal implementation is not implemented through static cache. So how does it implement an Activity that keeps existing objects after onDestroy?

The getViewModelStore method for ComponentActivity provides an answer:

    public ViewModelStore getViewModelStore(a) {
        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 = newViewModelStore(); }}return mViewModelStore;
    }
 
Copy the code

The purpose of the getViewModeStrore method is simple: to get a ViewModelStrore object. So where can I get this ViewModelStore? We can find two places in the code above:

  1. NonConfigurationInstances can get it.
  2. Create a new ViewModelStore object.

The second point we don’t have to see, the key is NonConfigurationInstances. NonConfigurationInstances what is this? Here, I explain the NonConfigurationInstances:

NonConfigurationInstances is a Wrapper for packaging because the data is not affected by the configuration changes, including the Fragment we are very familiar with, for example, an Activity with a Fragment, Rotating the screen causes the Activity to be recreated, so that the Activity is not the same object as before, but the Fragment is the same. This is achieved by NonConfigurationInstances (interested students can understand the fragments of the case, it is quite important, it can’t explain.) .

The above content looks exactly like what we want. Yes, ViewModelStore in Activity before and after reconstruction can keep the same object is achieved by NonConfigurationInstances. That is inside the getViewModelStore method, from NonConfigurationInstances ViewModelStore object is an Activity. At the same time, we can also see some code in ComponentActivity:

getLifecycle().addObserver(new LifecycleEventObserver() { @Override public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { if (event == Lifecycle.Event.ON_DESTROY) { if (! isChangingConfigurations()) { getViewModelStore().clear(); }}}});Copy the code

From the above code, we can see that if the Activity is called back to onDestroy due to a configuration change, it does not clear the contents of the ViewModelStore. This ensures that when the Activity is rebuilt due to a configuration change, the ViewModel object it recreates is the same object it created before. On the other hand, if the Activity is destroyed normally, the previously created ViewModel object is not saved. Instead, the ViewModelStore clear method is called. The clear method is also related to the coroutine in Kotlin, which is not explained here. If you are interested, you can look at viewModel.viewModelScope.