Preface
IM has been implemented in the last month. The UI framework uses the storage and notification mechanism of ViewModel and LiveData to make the code succinct and easy to maintain.
While the Android arch components control is powerful, we need to analyze a wave of source code implementation by the way. Today let's first analyze the source implementation of ViewModel.
Introduction to ViewModel
ViewModel stores and manages UI-related data by associating lifecycles. Even if the configuration changes, the data stored in the ViewModel will not be destroyed.
Without ViewModel, if the system Configuration changes, our Activity will be destroyed and rebuilt, resulting in loss of UI data in Activity. In order to avoid this problem, we can only save UI data in onSaveInstanceState(), and judge whether we have stored data in savedInstanceState in onCreate method.
With the ViewModel, we just need to store the data in the ViewModel, and the data in the ViewModel will not disappear with the destruction and reconstruction of the Activity. At the same time, if different Fragment s use the same Activity object to obtain ViewModel, data sharing and communication can be easily realized.
Examples using ViewModel:
// Customize a ViewModel to store a string public class TestViewModel extends ViewModel { public String content; @Override protected void onCleared() { // Data Cleaning content = null; } } // Get and use ViewModel in Activity TestViewModel viewModel = ViewModelProviders.of(activity).get(TestViewModel.class); Log.d("tag", viewModel.content);
Three questions
- How was ViewModel created?
- Why can different Fragment s use the same Activity object to get ViewModel and easily share ViewModel?
- Why can't ViewModel be destroyed and recycled when Activity destroys and rebuilds?
My personal habit is that when I look at the source code, I always have to analyze and understand it with problems, so that I can get some results.
Source code analysis
How was ViewModel created?
After removing the chain call from the sample code, we can see that the ViewModel is created by two steps as follows:
// 1. Create ViewModel Provider ViewModelProvider viewModelProvider = ViewModelProviders.of(activity); // 2. Obtaining ViewModel by Reflection TestViewModel viewModel = viewModelProvider.get(TestViewModel.class);
Create ViewModel Provider
Look at the first step of source code implementation:
public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) { if (factory == null) { // If the incoming object creation factory class is null, the default AndroidViewModelFactory is used to create the object. factory = AndroidViewModelFactory.getInstance(application); } // Create a ViewModel Provider return new ViewModelProvider(ViewModelStores.of(activity), (Factory)factory); }
In the source code, we found that creating a ViewModel Provider requires passing in two parameters: ViewModel Store and Factory. Let's first look at the implementation of Factory.
Factory
Factory, as its name implies, defines the behavior interface for creating the ViewModel. There is only one create method, which is used by the subclass to decide how to create a ViewModel object.
public interface Factory { <T extends ViewModel> T create(@NonNull Class<T> modelClass); }
At the same time, two default Factory implementations are provided inside the ViewModel Provider source code: New Instance Factory and Android View Model Factory.
// Create ViewModel by directly reflecting the parametric constructor of Class objects public static class NewInstanceFactory implements Factory { @Override public <T extends ViewModel> T create(@NonNull Class<T> modelClass) { 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); } } } // 1. If the object inherits from the AndroidViewModel, the constructor with Application parameters is launched to create the ViewModel object. // 2. If the object does not inherit from the AndroidViewModel, it directly calls the parent class, which calls the Class's parametric constructor to create the ViewModel object. public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory { private static AndroidViewModelFactory sInstance; @NonNull public static AndroidViewModelFactory getInstance(@NonNull Application application) { if (sInstance == null) { sInstance = new AndroidViewModelFactory(application); } return sInstance; } private Application mApplication; 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); } }
Enlightenment: If our custom ViewModel object needs various other parameters in the constructor, we just need to create a custom Factory class, and then call the class's parametric constructor to create it.
ViewModelStore
The ViewModelStore is a HashMap that retrieves the ViewModel object through the key.
public class ViewModelStore { private final HashMap<String, ViewModel> mMap = new HashMap<>(); final void put(String key, ViewModel viewModel) { ViewModel oldViewModel = mMap.get(key); if (oldViewModel != null) { oldViewModel.onCleared(); } mMap.put(key, viewModel); } final ViewModel get(String key) { return mMap.get(key); } }
ViewModelProvider
After understanding the Factory implementation and the ViewModel Store implementation, let's look at how ViewModel Provider's get method creates ViewModel objects.
public class ViewModelProvider { private final Factory mFactory; private final ViewModelStore mViewModelStore; public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) { mFactory = factory; this.mViewModelStore = store; } public <T extends ViewModel> T get(@NonNull Class<T> modelClass) { String canonicalName = modelClass.getCanonicalName(); 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)) { return (T) viewModel; } viewModel = mFactory.create(modelClass); mViewModelStore.put(key, viewModel); //noinspection unchecked return (T) viewModel; } }
ViewModleProvider's get method is also relatively simple to implement. To sum up, it is as follows:
- Canonical Name of ViewModel Class is used as the unique identifier of ViewModel in ViewModel Store.
- With unique identifiers, first query the ViewModel Store for the ViewModel object, and if so, return it directly.
- If the ViewModel object is not present in the ViewModel Store, the ViewModel object is created through the Factory Factory class reflection, stored in the ViewModel Store and returned to the caller.
Summary
So far, the first question, how ViewModel was created, has been analyzed. Next, let's look at the second question.
Why can different Fragment s use the same Activity object to get ViewModel and easily share ViewModel?
Reasonably, if the students carefully look at the process of creating ViewModel, this problem will be solved naturally.
Because different Fragments use the same Activity object to get the ViewModel, they will first query the ViewModel Store provided by Activity for the existence of the ViewModel object before creating the ViewModel. So we just need to call the ViewModel fetch code once in Activity to make the ViewModel exist in the ViewModel Store, so that different Fragments can share a ViewModel.
Why can't ViewModel be destroyed and recycled when Activity destroys and rebuilds?
Before looking at this, let's go back to the ViewModel Providers. of () source code:
public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) { if (factory == null) { // If the incoming object creation factory class is null, the default AndroidViewModelFactory is used to create the object. factory = AndroidViewModelFactory.getInstance(application); } // Create a ViewModel Provider return new ViewModelProvider(ViewModelStores.of(activity), (Factory)factory); }
ViewModel Provider obtains ViewModel Store through ViewModel Stores. of (activity). Let's first look at what this ViewModel Stores does.
ViewModelStores
Look at the name like a wrapped factory class. Look at the source code:
public static ViewModelStore of(@NonNull FragmentActivity activity) { return activity instanceof ViewModelStoreOwner ? ((ViewModelStoreOwner)activity).getViewModelStore() : HolderFragment.holderFragmentFor(activity).getViewModelStore(); }
From the source code, it can be found that if Activity is the implementation class of ViewModel StoreOwner, ViewModel Store is obtained directly through activity, if not through HolderFragment. holder FragmentFor (activity). getViewModel Store ().
By looking at the FragmentActivity source code, it is found that it has implemented the ViewModel StoreOwner interface.
public class FragmentActivity extends BaseFragmentActivityApi16 implements ViewModelStoreOwner, ActivityCompat.OnRequestPermissionsResultCallback, ActivityCompat.RequestPermissionsRequestCodeValidator { }
So we don't need to pay attention to Holder Fragment anymore. Many articles analyzing ViewModel source code spend a lot of time analyzing Holder Fragment. I really don't understand why this operation is needed. (Initial suspicions are that they are copying each other.) ).
Although FragmentActivity implements the ViewModelStoreOwner interface to provide the ViewModelStore, how does the ViewModelStore relate to the Activity life cycle?
Life Cycle Processing of ViewModel Store in FragmentActivity
A search of the call to ViewModel Store in FragmentActivity reveals that the implementation here should be related to lifecycle processing.
google searched for the role of Activity's onRetainNonConfiguration Instance: Most students knew that Activity would call onSave Instance State and onRestore Instance State when it was destroyed and rebuilt due to configuration changes. At the same time, Activity actually calls back onRetainNonConfiguration Instance and getLastNonConfiguration Instance methods.
The onRetainNonConfiguration Instance and onSave InstanceState work the same way to save UI-related variables. When Activity is accidentally destroyed, the ViewModelStore object of Activity is saved here.
When will the recovery take place?
When an Activity's onCreate call is made, getLastNonConfiguration Instance is called to retrieve the previously saved ViewModel Store and assign values if the ViewModel Store is not empty. The ViewModel Store is restored here.
Summary
Here we learned about two life cycle related function calls of Activity: onRetainNonConfiguration Instance and getLastNonConfiguration Instance.
- Activity implements the ViewModelStoreOwner interface and creates the ViewModelStore object.
- When Activity is accidentally destroyed, the onRetainNonConfiguration Instance function is called back, where the ViewModelStore object is saved.
- When Activity is rebuilt, the onCreate method first gets getLastNonConfiguration Instance, and if the ViewModelStore object is not empty, it references it directly and no longer recreates the ViewModelStore object.
summary
After analyzing the three problems, I believe you are familiar with the implementation principle of ViewModel. It is suggested that when you learn the source code in the future, you should also take the problem to analyze and think, and get twice the result with half the effort.