SoFunction
Updated on 2025-03-11

Android Jetpack library analysis of ViewModel components

Preface

Today, let’s explore the implementation principle of ViewModel together. Please forgive me for the wrong or insufficient description.

Introduction to ViewModel

ViewModel is an architectural component that can perceive the life cycle of Activity or Fragment. When the view is destroyed, the data will also be cleared. Therefore, its essence is to store data related to the view, so that the view display control is separated from the data. Even if the interface configuration changes, the data will not be destroyed. It is usually used with LiveData.

ViewModel usage

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        (savedInstanceState)
        val binding = ((baseContext))
        setContentView()
        //Get ViewModel instance        val viewModel: TextViewModel = ViewModelProvider(this).get(TextViewModel::)
        //Subscribe to data        (this, { println(it) })
        //Calling the function        ()
    }
    class TextViewModel : ViewModel() {
        val liveData = MediatorLiveData<String>()
        fun test() {
            ("Hello")
        }
    }
}

1. Use ViewModelProvider to get the ViewModel instance

2. Subscribe to VIewModel LiveData

3. Call the ViewModel method

What does the process of constructing a ViewModelProvider do?

    public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
        this((), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : ());
    }
    public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    

1. When we create a ViewModelProvider, we need to pass in a ViewModelStoreOwner object. ViewModelStoreOwner is responsible for providing the ViewModelStore object. ComponentActivity implements this interface, so we can pass the current Activity by default.

2. First, determine whether there is a default ViewModel factory. If not, create a Factory

3. Factory is used to create ViewModel, ViewModelStore is used to store ViewModel

How to build a ViewModel by calling get() method

    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
        //Get the class name        String canonicalName = ();
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }
    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        //Get into ViewModelStore via key        ViewModel viewModel = (key);
        
        //If you get the ViewModel, it means that this ViewModel has been created and returns directly        if ((viewModel)) {
            if (mFactory instanceof OnRequeryFactory) {
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        // Create an instance using reflection through Factor        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
        } else {
            viewModel = (modelClass);
        }
        //Save the ViewModel instance in the ViewModelStore        (key, viewModel);
        return (T) viewModel;
    }

1. When we call the get() method, we will first get the class name and then generate a unique key.

2. First, use the key to get the ViewModelStore. If it is not empty, it means that the instance of this ViewModel has been created, and then return this instance directly.

3. If it is empty, create an instance through the factory using the Java reflection mechanism, save it to the ViewModelStore through key-value pairs, and return the instance. When we see this, we suddenly understand why multiple fragments can share the same ViewModel, because the Fragment is attached to the Activity. When we build the ViewModelProvider, the ViewModelStore obtained by the ViewModelProvider is the same. When we call the get() method, we can get the same ViewModel instance into the ViewModelStore through key. To put it bluntly, it is the ViewModel that shares the Activity.

Activity configuration changes how to cache ViewModelStore

ActivityThread{
    private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
            List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
            PendingTransactionActions pendingActions, boolean startsNotResumed,
            Configuration overrideConfig, String reason) {
        // Preserve last used intent, it may be set from Activity#setIntent().
        final Intent customIntent = ;
        // Need to ensure state is saved.
        if (!) {
            performPauseActivity(r, false, reason, null /* pendingActions */);
        }
        if (!) {
            callActivityOnStop(r, true /* saveState */, reason);
        }
        //Destroy Activity        handleDestroyActivity(, false, configChanges, true, reason);
        //Start Activity        handleLaunchActivity(r, pendingActions, customIntent);
    }
}

1. When we switch horizontal and vertical screens, ActivityThread will receive a RELAUNCH_ACTIVITY message and will call its own handleRelaunchActivityInner(). There is a parameter r in this method, which is type ActivityClientRecord. Every time we open an Activity, ActivityThread will generate such an object to record the Activity we open and save it.

2. The handleRelaunchActivityInner() method is called to destroy our Activity

ActivityThread{
        @Override
    public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
            boolean getNonConfigInstance, String reason) {
        //Execute the destruction of the activity        ActivityClientRecord r = performDestroyActivity(token, finishing,
                configChanges, getNonConfigInstance, reason);
    }
    ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
            int configChanges, boolean getNonConfigInstance, String reason) {
        //Get Activity records through token        ActivityClientRecord r = (token);
        if (r != null) {
            //Does it be necessary to obtain the configuration instance?            if (getNonConfigInstance) {
                try {
                    //Calling the retainNonConfigurationInstances method of Activity to get the configuration instance                    
                            = ();
                } catch (Exception e) {
                }
            }
            (ON_DESTROY);
        }
        //Remove this Activity record through token        synchronized (mResourcesManager) {
            (token);
        }
        return r;
    }
}

3. handleDestroyActivity() directly calls the performDestroyActivity() method to destroy the Activity. The core part is to call the retainNonConfigurationInstances() method of Activity to obtain the configuration instance and copy it to ActivityClientRecord, and save the NonConfigurationInstances instance.

Activity{
    NonConfigurationInstances retainNonConfigurationInstances() {
        //Get NonConfigurationInstances object        Object activity = onRetainNonConfigurationInstance();
        HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
        FragmentManagerNonConfig fragments = ();
        ();
        (true);
        ArrayMap<String, LoaderManager> loaders = ();
        //Create NonConfigurationInstances instance        NonConfigurationInstances nci = new NonConfigurationInstances();
         = activity;
         = children;
         = fragments;
         = loaders;
        if (mVoiceInteractor != null) {
            ();
             = mVoiceInteractor;
        }
        return nci;
    }
}
ComponentActivity{
    @Override
    @Nullable
    public final Object onRetainNonConfigurationInstance() {
        Object custom = onRetainCustomNonConfigurationInstance();
        //Get ViewModelStore        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 = ;
            }
        }
        if (viewModelStore == null && custom == null) {
            return null;
        }
        //Create NonConfigurationInstances instance        NonConfigurationInstances nci = new NonConfigurationInstances();
         = custom;
         = viewModelStore;
        return nci;
    }
}

4. By looking up the source code and analyzing it layer by layer, ActivityThread is used to obtain our ViewModelStore instance and save it in ActivityClientRecord.

How to restore ViewModelStore after Activity reconstruction

ActivityThread{
    private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
            List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
            PendingTransactionActions pendingActions, boolean startsNotResumed,
            Configuration overrideConfig, String reason) {
        // Preserve last used intent, it may be set from Activity#setIntent().
        final Intent customIntent = ;
        // Need to ensure state is saved.
        if (!) {
            performPauseActivity(r, false, reason, null /* pendingActions */);
        }
        if (!) {
            callActivityOnStop(r, true /* saveState */, reason);
        }
        //Destroy Activity        handleDestroyActivity(, false, configChanges, true, reason);
        //Start Activity        handleLaunchActivity(r, pendingActions, customIntent);
    }
}

1. When the handleDestroyActivity is executed, the instance of ViewModelStore has been retrieved and stored in the ActivityClientRecord. At this time, the handleLaunchActivity() method is started to be executed to start the activity.

2. The handleLaunchActivity() method also requires the ActivityClientRecord parameter. At this time, the ActivityClientRecord object is the object that obtains and saves the ViewModelStore instance through the handleDestroyActivity() method.

3. HandleLaunchActivity() calls the performLaunchActivity() method to start the Activity

ActivityThread{
    @Override
    public Activity handleLaunchActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, Intent customIntent) {
        // Make sure we are running with the most recent config.
        handleConfigurationChanged(null, null);
        //Go to start Activity        final Activity a = performLaunchActivity(r, customIntent);
        return a;
    }
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        try {
            Application app = (false, mInstrumentation);
            if (activity != null) {
                CharSequence title = (());
                Configuration config = new Configuration(mCompatConfiguration);
                if ( != null) {
                    ();
                }
                if (DEBUG_CONFIGURATION) (TAG, "Launching activity "
                        +  + " with config " + config);
                Window window = null;
                if ( != null && ) {
                    window = ;
                     = null;
                     = null;
                }
                (activity);
                //Calling the attachment() method of the currently opened Activity                (appContext, this, getInstrumentation(), ,
                        , app, , , title, ,
                        , , config,
                        , , window, ,
                        );
                 = activity;
            }
            (ON_CREATE);
            //Save this Activity record            synchronized (mResourcesManager) {
                (, r);
            }
        } catch (SuperNotCalledException e) {
            throw e;
        } catch (Exception e) {
        }
        return activity;
    }
}

4. Through the code, it is found that performLaunchActivity calls the attachment method of the Activity that is currently being opened, and this method requires a parameter of NonConfigurationInstances type. This parameter contains our ViewModelStore instance.

Activity{
     final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
        attachBaseContext(context);
        (null /*parent*/);
        // Assign value to mLastNonConfigurationInstances        mLastNonConfigurationInstances = lastNonConfigurationInstances;
        setAutofillOptions(());
        setContentCaptureOptions(());
    }
    public Object getLastNonConfigurationInstance() {
        return mLastNonConfigurationInstances != null
                ?  : null;
    }
}
ComponentActivity{
    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) {
            //Go to lastNonConfigurationInstance first, and then create it again            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = ;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }
}

5. In the attach method, assign this parameter to mLastNonConfigurationInstances. When we obtain the ViewModelStore instance, we will first go to mLastNonConfigurationInstances. If we do not create a ViewModelStore instance by ourselves, we will understand the entire call chain here.

Lifecycle binding

ComponentActivity{
    public ComponentActivity(){
        //Subscribe to the life cycle, when the life cycle ==ON_DESTROY, clear the ViewModel data        getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull  event) {
                if (event == .ON_DESTROY) {
                    if (!isChangingConfigurations()) {
                        getViewModelStore().clear();
                    }
                }
            }
        });
    }
}
ViewModelStore{
    public final void clear() {
        //Transfuse all ViewModels and call its clear() method to clear the data        for (ViewModel vm : ()) {
            ();
        }
        //Clear all ViewModels        ();
    }
}

Summarize

1. ComponentActivity implements the ViewModelStoreOwner interface and implements its abstract method getViewModelStore()

2. We created a ViewModel using the default factory through the ViewModelProvider, identify it through a unique key value and save it to the ViewModelStore

3. When switching horizontal and vertical screens, ActivityThread receives the RELAUNCH_ACTIVITY message, and will call the retainNonConfigurationInstances() method of Activity to get our ViewModelStore instance and save it.

4. When the Activity is started and the attach() method is called, the ViewModel in front of the horizontal and vertical screen will be restored.

5. When we get the ViewModelStore instance, we will call the first getLastNonConfigurationInstance() method to get the ViewModelStore. If it is null, the ViewModelStore will be recreated and stored in the global

6. When the life cycle changes and the status is ON_DESTROY, clear ViewModel data and instances

This is the article about the ViewModel component analysis of Android Jetpack library. This is all about this article. For more related Android ViewModel content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!