The ViewModel overview
The ViewModel class is designed to store and manage the interface’s data in a life-cycle focused way, allowing the data to survive configuration changes such as screen rotation. It also handles communication between the interface and other parts of the application (for example, invoking business logic classes). The interface should be able to observe changes in the ViewModel, usually exposing this information through LiveData or DataBinding, and it must never have a reference to the interface.
ViewModel source parsing
public abstract class ViewModel {
// Can't use ConcurrentHashMap, because it can lose values on old apis (see b/37042460)
@Nullable
private final Map<String, Object> mBagOfTags = new HashMap<>();
private volatile boolean mCleared = false;
/** * This method is called when the ViewModel is cleared * we can override this method and do some recycling in it */
@SuppressWarnings("WeakerAccess")
protected void onCleared(a) {}@MainThread
final void clear(a) {
mCleared = true;
if(mBagOfTags ! =null) {
synchronized (mBagOfTags) {
for (Object value : mBagOfTags.values()) {
// see comment for the similar call in setTagIfAbsent
closeWithRuntimeException(value);
}
}
}
onCleared();
}
@SuppressWarnings("unchecked")
<T> T setTagIfAbsent(String key, T newValue) {
T previous;
synchronized (mBagOfTags) {
previous = (T) mBagOfTags.get(key);
if (previous == null) {
mBagOfTags.put(key, newValue);
}
}
T result = previous == null ? newValue : previous;
if (mCleared) {
closeWithRuntimeException(result);
}
return result;
}
/** * Returns the tag associated with this viewmodel and the specified key. */
@SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"})
<T> T getTag(String key) {
if (mBagOfTags == null) {
return null;
}
synchronized (mBagOfTags) {
return(T) mBagOfTags.get(key); }}private static void closeWithRuntimeException(Object obj) {
if (obj instanceof Closeable) {
try {
((Closeable) obj).close();
} catch (IOException e) {
throw newRuntimeException(e); }}}}Copy the code
As you can see, the ViewModel source code is simply maintaining the mBagOfTags HashMap and clear() functions. Now let’s see where is clear() called
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);
}
Set<String> keys(a) {
return new HashSet<>(mMap.keySet());
}
/** * Clears internal storage and notifies ViewModels that they are no longer used. */
public final void clear(a) {
for(ViewModel vm : mMap.values()) { vm.clear(); } mMap.clear(); }}Copy the code
Is called inside the Clear () of the ViewModelStore. You can see that ViewModelStore is the class that manages viewModels. The ViewModelStore’s clear() is called in the Activity’s constructor
public ComponentActivity(a) {
// Omit the code...
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(); }}}});// Omit the code...
}
Copy the code
GetViewModelStore ().clear() is called when the Activity onDistroy is not a configuration change and the ViewModel is cleared. And then let’s see who put the ViewModel in.
public class ViewModelProvider {
private static final String DEFAULT_KEY =
"androidx.lifecycle.ViewModelProvider.DefaultKey";
// Omit the code...
@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);
}
@SuppressWarnings("unchecked")
@NonNull
@MainThread
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;
}
// Omit the code...
}
Copy the code
And you can see that the get method basically just returns if it exists, and if it doesn’t it creates a ViewModel and saves it and returns it. Here we use two objects, mViewModelStore and mFactory, which are instantiated by the ViewModelProvider class in the constructor. Let’s look at its constructor.
public class ViewModelProvider {
// Omit the code...
private final Factory mFactory;
private final ViewModelStore mViewModelStore;
// Constructor 1
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
// Omit the code...
}
Copy the code
We usually use constructor 1 to pass in the ViewModelStoreOwner object, which is an interface class
@SuppressWarnings("WeakerAccess")
public interface ViewModelStoreOwner {
/**
* Returns owned {@link ViewModelStore}
*
* @return a {@code ViewModelStore}
*/
@NonNull
ViewModelStore getViewModelStore(a);
}
Copy the code
We can see that the Activity/Fragment implements this interface. You can see “owner instanceof HasDefaultViewModelProviderFactory” also has a possible HasDefaultViewModelProviderFactory this interface.
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
LifecycleOwner.ViewModelStoreOwner.HasDefaultViewModelProviderFactory.SavedStateRegistryOwner.OnBackPressedDispatcherOwner {
Copy the code
It is so, and here we will look at HasDefaultViewModelProviderFactory under this interface
public interface HasDefaultViewModelProviderFactory {
/**
* Returns the default {@link ViewModelProvider.Factory} that should be
* used when no custom {@code Factory} is provided to the
* {@link ViewModelProvider} constructors.
*
* @return a {@code ViewModelProvider.Factory}
*/
@NonNull
ViewModelProvider.Factory getDefaultViewModelProviderFactory(a);
}
Copy the code
After this we look at the Activity of ViewModelStoreOwner, HasDefaultViewModelProviderFactory interface methods
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) {
/ / viewModelStore recovery
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = newViewModelStore(); }}return mViewModelStore;
}
public ViewModelProvider.Factory getDefaultViewModelProviderFactory(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 (mDefaultFactory == null) {
mDefaultFactory = new SavedStateViewModelFactory(
getApplication(),
this, getIntent() ! =null ? getIntent().getExtras() : null);
}
return mDefaultFactory;
}
Copy the code
Let’s start with getViewModelStore(), which normally creates an instance on the first call and gets it directly on the next call. NonConfigurationInstances main effect of this class is, in the event of a screen rotation, or some configuration changes will need to save the instance on the inside, and passed on to the next Activity (restoration Activity), these are instances in this situation without having to create; Such as ViewModelStore
GetDefaultViewModelProviderFactory () calls for the first time will create a SavedStateViewModelFactory object. As the name suggests, it’s a ViewModel factory that saves the state, and I won’t show you the code. The main logic is to create a ViewModel object through reflection if you don’t need SavedStateHandle. Need SavedStateHandle situation you can look at this article www.jianshu.com/p/731ca4282…
summary
Above we know from the ViewModel source reverse push to know the creation of the ViewModel, restore and clear process.
So we know why the ViewModel is not recreated when the Activity changes its configuration, such as screen rotation; Why does the ViewModel have a longer lifecycle than the Activity? Those of you who don’t know can watch it again.