Component schema ViewModel
1.What?
1. Classes that can be used to manage and prepare data, usually used for fragment and activity (implements ViewModelStoreOwner, and provides ViewModelStore by default).
2. As a bridge of information communication (including activity, fragment and other application components).
3. The life cycle of the ViewModel is destroyed only when the ViewModelStore is destroyed.
The above is only a personal summary of the official Chinese New Year.
2. Usage
1. Create the ViewModel to be saved
public class BoyiKiaViewModel extends ViewModel { }
2. Create a provider and obtain the ViewModel instance saved in the ViewModelStore through the provider
ViewModelProvider provider=new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()); BoyiKiaViewModel boyiKiaViewModel = provider.get(BoyiKiaViewModel.class);
3.ViewModel
When the viewModel is destroyed, the onCleared method is executed
public abstract class ViewModel { /** * This method will be called when this ViewModel is no longer used and will be destroyed. * <p> * 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() { } }
1. ViewModel associated with application
public class AndroidViewModel extends ViewModel { @SuppressLint("StaticFieldLeak") private Application mApplication; public AndroidViewModel(@NonNull Application application) { mApplication = application; } /** * Return the application. */ @SuppressWarnings("TypeParameterUnusedInFormals") @NonNull public <T extends Application> T getApplication() { //noinspection unchecked return (T) mApplication; } }
4.ViewModelProvider
Constructor
Binding the constructor and ViewStore of ViewModel
//1. public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) { mFactory = factory; this.mViewModelStore = store; } //2. Get the viewstore through ViewModelStoreOwner (a modelstoreowner is implemented by default when the activity is created) public interface ViewModelStoreOwner { /** * Returns owned {@link ViewModelStore} * * @return a {@code ViewModelStore} */ @NonNull ViewModelStore getViewModelStore(); } public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) { this(owner.getViewModelStore(), factory); }
Factory
ViewModel instance builder
public interface Factory { /** * Creates a new instance of the given {@code Class}. * <p> * * @param modelClass a {@code Class} whose instance is requested * @param <T> The type parameter for the ViewModel. * @return a newly created ViewModel */ @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass); }
Default implemented constructor
public static class NewInstanceFactory implements Factory { @SuppressWarnings("ClassNewInstance") @NonNull @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); } } }
/** * {@link Factory} which may create {@link AndroidViewModel} and * {@link ViewModel}, which have an empty constructor. */ 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); } }
Get ViewModel instance
Get the ViewModel from the viewModelStore. If not, instance a ViewModel and put it into the viewModelStore
//ViewModelProvider.java private static final String DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey"; @NonNull @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); } @NonNull @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; }
5.ViewModelStore
A hash table is maintained by default
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. */ //Destroy all stored viewmodels public final void clear() { for (ViewModel vm : mMap.values()) { vm.onCleared(); } mMap.clear(); } }
6. Component architecture of activity and fragment
A ViewModelStore is maintained by default in activity
//FragmentActivity.java @NonNull @Override 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 nc = (NonConfigurationInstances) getLastNonConfigurationInstance(); if (nc != null) { // Restore the ViewModelStore from NonConfigurationInstances mViewModelStore = nc.viewModelStore; } if (mViewModelStore == null) { mViewModelStore = new ViewModelStore(); } } return mViewModelStore; } //Destroy viewModelStore @Override protected void onDestroy() { super.onDestroy(); if (mViewModelStore != null && !isChangingConfigurations()) { mViewModelStore.clear(); } mFragments.dispatchDestroy(); }
ViewModelStore held by fragment by default
//fragment.java @NonNull @Override public ViewModelStore getViewModelStore() { if (getContext() == null) { throw new IllegalStateException("Can't access ViewModels from detached fragment"); } if (mViewModelStore == null) { mViewModelStore = new ViewModelStore(); } return mViewModelStore; } //Destroy viewModelStore @CallSuper public void onDestroy() { if (mViewModelStore != null && !isChangingConfigurations) { mViewModelStore.clear(); } }
7 Summary
- Different components, as long as they hold the same ViewModelStore, can share data by acquiring ViewModel
- Activities and fragments, or fragments under the same host, can share arrays. You can use the host's viewModelStore
- viewModelStore in activity and fragment is destroyed only when the component is destroyed.