DEV Community

Ayush Bansal
Ayush Bansal

Posted on

ViewModel & Co.

Let's dive into the relationship between ViewModel, ViewModelProvider, ViewModelStoreOwner, and ViewModelStore by examining their roles and interactions within the AOSP codebase.

Think of these four components working together as a system for managing and persisting UI data across configuration changes like screen rotations.


The Core Components Explained

1. ViewModel ๐Ÿง 

This is the class you'll interact with most directly. Its primary job is to hold and manage UI-related data. The key feature of a ViewModel is that it survives configuration changes that would normally destroy and recreate an Activity or Fragment.

When a ViewModel is no longer needed (because its associated UI component is permanently destroyed), its onCleared() method is called to free up any resources.

2. ViewModelStore ๐Ÿ—„๏ธ

This is a simple container class. As its name suggests, its only job is to store ViewModel instances. Under the hood, it uses a HashMap<String, ViewModel> to map a key (usually derived from the ViewModel's class name) to the ViewModel instance itself.

From the AOSP source, we can see its core implementation:

// A simplified view from the source
public class ViewModelStore {
    private final HashMap<String, ViewModel> mMap = new HashMap<>();

    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }

    final ViewModel get(String key) {
        return mMap.get(key);
    }

    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.onCleared();
        }
        mMap.clear();
    }
}
Enter fullscreen mode Exit fullscreen mode

3. ViewModelStoreOwner ๐Ÿง‘โ€๐Ÿ’ผ

This is an interface that provides a ViewModelStore to other objects. Any class that implements ViewModelStoreOwner is responsible for two things:

  1. Providing a ViewModelStore via the getViewModelStore() method.
  2. Retaining that ViewModelStore instance across configuration changes and calling its clear() method when the scope is permanently destroyed.

The most common implementers of this interface are ComponentActivity and Fragment. They handle the logic of saving and restoring the ViewModelStore for you.

// The simple interface definition
public interface ViewModelStoreOwner {
    @NonNull
    ViewModelStore getViewModelStore();
}
Enter fullscreen mode Exit fullscreen mode

4. ViewModelProvider ๐Ÿญ

This is the factory and retriever for ViewModels. You don't create ViewModels directly; you ask a ViewModelProvider for one. It's responsible for either creating a new ViewModel instance or, more importantly, retrieving an existing one from the ViewModelStore.

When you create a ViewModelProvider, you give it a ViewModelStoreOwner (like an Activity or Fragment). The provider then uses the owner to get access to the ViewModelStore.

// One of the main constructors
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
    this(owner.getViewModelStore(), factory);
}

// The core 'get' method logic
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    ViewModel viewModel = mViewModelStore.get(key); // Check the store first

    if (modelClass.isInstance(viewModel)) {
        // ViewModel already exists, return it
        return (T) viewModel;
    }

    // ViewModel doesn't exist, create it using the factory
    viewModel = mFactory.create(modelClass);
    mViewModelStore.put(key, viewModel); // Put the new instance in the store
    return (T) viewModel;
}
Enter fullscreen mode Exit fullscreen mode

The Relationship: How They Work Together

Here's the step-by-step flow that connects all four components:

  1. Your Activity or Fragment starts. Since it implements ViewModelStoreOwner, it either creates a new ViewModelStore or retrieves a previously saved one (if a configuration change just happened).

  2. To get your ViewModel, you instantiate a ViewModelProvider, passing your Activity/Fragment (this) as the ViewModelStoreOwner.

  3. The ViewModelProvider's constructor calls getViewModelStore() on your Activity/Fragment to get a reference to the ViewModelStore.

  4. You call viewModelProvider.get(MyViewModel.class).

  5. The ViewModelProvider asks the ViewModelStore if it already has an instance for the key associated with MyViewModel.

  6. If yes, the existing ViewModel instance is returned. This is what happens during a screen rotation.
    If no, the ViewModelProvider uses its factory to create a new MyViewModel instance, adds it to the ViewModelStore, and then returns it.

  7. When your Activity/Fragment is permanently destroyed (e.g., the user presses the back button and finishes it), its ViewModelStoreOwner implementation calls clear() on the ViewModelStore.

  8. The ViewModelStore then calls onCleared() on every ViewModel it holds, allowing them to clean up their resources.

In essence, the ViewModelStoreOwner (the UI controller) owns the ViewModelStore (the storage). The ViewModelProvider (the factory) acts as the middleman to create or retrieve ViewModels from that storage, ensuring you always get the correct instance for the given owner's lifecycle.

Top comments (0)