This is the 15th day of my participation in Gwen Challenge

The ViewModel overview

Introduction to the

A ViewModel is a class designed to store and manage UI-related data in a life-cycle-aware manner. It allows data to survive configuration changes, such as screen rotation, without being killed.

define

The ViewModel is an abstract class in which only an empty implementation of the onCleared() method is defined.

public abstract class ViewModel {
    /** * This method will be called when this ViewModel is no longer used and will be destroyed. * 

* It is useful when ViewModel observes some data and you need to clear this subscription to * prevent a leak of this ViewModel. */

@SuppressWarnings("WeakerAccess") protected void onCleared(a) {}}Copy the code

Do not reference the View, or any context that holds the Activity class, in the ViewModel; this will cause memory leaks.

When the ViewModel needs the Context of the Application class to get resources, find system services, etc., it can inherit the AndroidViewModel class.

public class AndroidViewModel extends ViewModel {
    @SuppressLint("StaticFieldLeak")
    private Application mApplication;

    public AndroidViewModel(@NonNull Application application) {
        mApplication = application;
    }

    /** * Return the application. */
    @NonNull
    public <T extends Application> T getApplication(a) {
        //noinspection unchecked
        return(T) mApplication; }}Copy the code

The ViewModel use demo

Take a look at the source code for an example

public class UserActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.user_activity_layout);
        final UserModel viewModel = ViewModelProviders.of(this).get(UserModel.class);
        viewModel.userLiveData.observer(this.new Observer<User>() {
           @Override
            public void onChanged(@Nullable User data) {
                // update ui.}}); findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) { viewModel.doAction(); }}); }}Copy the code
public class UserModel extends ViewModel {
    public final LiveData<User> userLiveData = new LiveData<>();
    public UserModel(a) {
        // trigger user load.
    }
    void doAction(a) {
        // depending on the action, do necessary business logic calls and update the
        // userLiveData.}}Copy the code

Source code analysis

ViewModel is an abstract class with an onCleared() method

Let’s look at the code to get the ViewModel

ViewModelProviders.of(this).get(UserModel.class)

ViewModelProviders

The ViewModelProviders class provides four static factory methods of() to create new ViewModelProvider objects.

ViewModelProviders.of(Fragment) 
ViewModelProviders.of(FragmentActivity)
ViewModelProviders.of(Fragment, Factory) 
ViewModelProviders.of(FragmentActivity, Factory)
Copy the code
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
    Application application = checkApplication(checkActivity(fragment));
    if (factory == null) {
        factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }
    return new ViewModelProvider(fragment.getViewModelStore(), factory);
}
Copy the code
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
        @Nullable Factory factory) {
    Application application = checkApplication(activity);
    if (factory == null) {
        factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }
    return new ViewModelProvider(activity.getViewModelStore(), factory);
}
Copy the code

ViewModelProvider

The ViewModelProvider is responsible for providing ViewModel objects

public class ViewModelProvider {

    public interface Factory {
       
        <T extends ViewModel> T create(@NonNull Class<T> modelClass);
    }

    private final Factory mFactory;
    private final ViewModelStore mViewModelStore;
    
    / /... Omit other extraneous code
}
Copy the code

The Factory interface defines an interface to create a ViewModel, which the ViewModelProvider calls when needed to create a new ViewModel object.

Android already has two built-in Factory implementation classes, respectively:

  • AndroidViewModelFactoryImplementation classes that can be createdViewModelAndroidViewModelSubclass object.
  • NewInstanceFactoryClass that can only be createdViewModelSubclass object.

They are both implemented by calling the constructor of a ViewModel subclass via reflection to create an object.

public static class NewInstanceFactory implements Factory {
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        //noinspection TryWithIdenticalCatches
        try {
            return modelClass.newInstance();
        } catch (InstantiationException e) {
            throw new RuntimeException("Cannot create an instance of " + modelClass, e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Cannot create an instance of "+ modelClass, e); }}}Copy the code

AndroidViewModelFactory inherits the NewInstanceFactory class, which is a singleton and supports the creation of AndroidViewModel subclass objects.

public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {

    private static AndroidViewModelFactory sInstance;

    /**
     * Retrieve a singleton instance of AndroidViewModelFactory.
     *
     * @param application an application to pass in {@link AndroidViewModel}
     * @return A valid {@link AndroidViewModelFactory}
     */
    @NonNull
    public static AndroidViewModelFactory getInstance(@NonNull Application application) {
        if (sInstance == null) {
            sInstance = new AndroidViewModelFactory(application);
        }
        return sInstance;
    }

    private Application mApplication;

    /**
     * Creates a {@code AndroidViewModelFactory}
     *
     * @param application an application to pass in {@link AndroidViewModel}
     */
    public AndroidViewModelFactory(@NonNull Application application) {
        mApplication = application;
    }

    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
            //noinspection TryWithIdenticalCatches
            try {
                return modelClass.getConstructor(Application.class).newInstance(mApplication);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (InstantiationException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException("Cannot create an instance of "+ modelClass, e); }}return super.create(modelClass); }}Copy the code

ViewModelStore

Take a look at the ViewModelStore object you need to instantiate the ViewModelProvider

The ViewModelStore class maintains a Map

object that stores the created ViewModel objects and provides put() and get() methods.
,>

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);
    }

    /** * Clears internal storage and notifies ViewModels that they are no longer used. */
    public final void clear(a) {
        for(ViewModel vm : mMap.values()) { vm.onCleared(); } mMap.clear(); }}Copy the code

And this ViewModelStore code know, before it is passed through the incoming fragments/FragmentActivity take, these two classes implement ViewModelStoreOwner interface, Returns the ViewModelStore object in the current UI scope.

ViewModelStoreOwner

public interface ViewModelStoreOwner {
    /**
     * Returns owned {@link ViewModelStore}
     *
     * @return a {@code ViewModelStore}
     */
    @NonNull
    ViewModelStore getViewModelStore(a);
}
Copy the code

The implementation is as follows:

androidx.fragment.app.Fragment#getViewModelStore

@Override
public ViewModelStore getViewModelStore(a) {
    if (getContext() == null) {
        throw new IllegalStateException("Can't access ViewModels from detached fragment");
    }
    if (mViewModelStore == null) {
        mViewModelStore = new ViewModelStore();
    }
    return mViewModelStore;
}
Copy the code

androidx.fragment.app.FragmentActivity#getViewModelStore

private ViewModelStore mViewModelStore;

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

Access to the ViewModel

final UserModel viewModel = ViewModelProviders.of(this).get(UserModel.class);
Copy the code

Viewmodelprovider.of (this), as analyzed above, returns an instance of the ViewModelProvider and retrieves the ViewModel via get()

@MainThread
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
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    ViewModel viewModel = mViewModelStore.get(key);

    if (modelClass.isInstance(viewModel)) {
        //noinspection unchecked
        return (T) viewModel;
    } else {
        //noinspection StatementWithEmptyBody
        if(viewModel ! =null) {
            // TODO: log a warning.
        }
    }

    viewModel = mFactory.create(modelClass);
    mViewModelStore.put(key, viewModel);
    //noinspection unchecked
    return (T) viewModel;
}
Copy the code

If it doesn’t exist in ViewModelStore, create a new object using Factory and store it in ViewModelStore.

This is the main process of creating and retrieving a ViewModel

Create a ViewModelProvider object with ViewModelProviders and call its get() method to get the ViewModel object. When the desired object does not exist in the ViewModelStore, the ViewModelProvider creates a new object using Factory and stores it in the ViewModelStore.

Note: See The Android Jetpack ViewModel for the source code analysis of the older version

The ViewModel summary

Configuration ChangesPrinciple of survival

Juejin. Cn/post / 5 beccf…

There’s another paragraph in the summary

Destruction of the process

androidx.fragment.app.FragmentActivity#onDestroy

@Override
protected void onDestroy(a) {
    super.onDestroy();

    if(mViewModelStore ! =null && !isChangingConfigurations()) {
        mViewModelStore.clear();
    }

    mFragments.dispatchDestroy();
}
Copy the code

androidx.fragment.app.Fragment#onDestroy

@CallSuper
public void onDestroy(a) {
    mCalled = true;
    FragmentActivity activity = getActivity();
    booleanisChangingConfigurations = activity ! =null && activity.isChangingConfigurations();
    if(mViewModelStore ! =null&&! isChangingConfigurations) { mViewModelStore.clear(); }}Copy the code

If Configuration Changes have occurred, the ViewModelStore clear() method is called, and then the onCleared() method of each ViewModel is called.

public final void clear(a) {
    for (ViewModel vm : mMap.values()) {
        vm.onCleared();
    }
    mMap.clear();
}
Copy the code

The life cycle

The ViewModel object is limited to Lifecycle passed to the ViewModelProvider when it retrieves itself. Until this Lifecycle dies permanently, the ViewModel will remain in memory: like the activity’s finish, or the fragment’s detach.

The following figure shows the various lifecycle states that an activity goes through as it spins and terminates, along with the lifecycle of the ViewModel associated with it. The same basic state applies to the fragment’s life cycle.

In general, you can request a specific ViewModel the first time the system calls the onCreate() method of an activity object. The system may call the onCreate() method several times throughout the activity’s life, such as when the screen on the device rotates. The ViewModel exists from the time you first request it until the activity is terminated and destroyed.

use

  1. In Android, UI components such as activities and fragments are destroyed or rebuilt by the system, and unprocessed UI data is lost. In the past, we used onSaveInstanceState() to save THE UI data and restore the UI data in the onCreate() method (usually ensuring that it was properly serialized), but there were limits on the size and type of the data (e.g. List, Bitmap…).

    When Configuration Changes occur, the ViewModel can be used to restore data

  2. Activities/fragments often need to perform asynchronous operations. When it comes to asynchrony, we all understand the potential for memory leaks. Therefore, we ensure that the Activity/Fragment cleans up the asynchronous operation immediately after it is destroyed to avoid potential memory leaks.

    The ViewModel does not automatically solve this problem for us, but rather gives the business itself to rewrite through onCleared().

    When the UI component on which the ViewModel is located is actually destroyed, its onCleared() method is called and can override the method to clean up the resource.

  3. Fragments can handle communication problems by sharing viewModels with host activities.

    Viewmodels provided by the ViewModelStore in FragmentActivity can survive until the FragmentActivity is destroyed. Therefore, different Fragment instances can communicate with each other by passing FragmentActivity directly to the same ViewModel instance.

other

Lifecycles consists of three components, Lifecycle, LiveData and ViewModel. It can be used to solve common lifecycle problems while making your applications easy to test and maintainable.

It is recommended that developers use both ViewModel and LiveData, another lifecycle component, to implement responsive UI interfaces.

Note: Do not reference a View in the ViewModel, use LiveData to notify the interface of updates; Don’t confuse the ViewModel with the onSaveInstanceState method.

Cooperate with the Dagger/Koin

See when Koin hits ViewModel

reference

ViewModel Overview

Examine the ViewModel of Android architecture components

Bit by bit into the JetPack: ViewModel chapter