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:
AndroidViewModelFactory
Implementation classes that can be createdViewModel
和AndroidViewModel
Subclass object.NewInstanceFactory
Class that can only be createdViewModel
Subclass 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
-
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
-
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.
-
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