current position:Home>ViewModel of jetpack
ViewModel of jetpack
2022-01-27 03:50:21 【LiuShangYuan】
1、ViewModel
1.1、ViewModel summary
ViewModel Comments in class :
- ViewModel is a class that is responsible for preparing and managing the data for an Activity or a Fragment. It also handles the communication of the Activity / Fragment with the rest of the application (e.g. calling the business logic classes).
ViewModel It's a tool for preparing and managing Activity or Fragment Class of data in , It handles Activity and Fragment Communication with other parts of the application .
- A ViewModel is always created in association with a scope (a fragment or an activity) and will be retained as long as the scope is alive. E.g. if it is an Activity, until it is finished.In other words, this means that a ViewModel will not be destroyed if its owner is destroyed for a configuration change (e.g. rotation). The new owner instance just re-connects to the existing model.
ViewModel Always create with a Activity perhaps Fragment relation , As long as they are alive ,ViewModel There will always be .ViewModel Not because owner Configuration programming and destruction , new owner The example reconnects the existing ViewModel.
- The purpose of the ViewModel is to acquire and keep the information that is necessary for an Activity or a Fragment. The Activity or the Fragment should be able to observe changes in the ViewModel. ViewModels usually expose this information via LiveData or Android Data Binding. You can also use any observability construct from you favorite framework.
......,Activity or Fragment Should be able to observe ViewModel Changes ,ViewModel Usually by LiveData To expose this information ,......
- ViewModel's only responsibility is to manage the data for the UI. It should never access your view hierarchy or hold a reference back to the Activity or the Fragment.
ViewModel Only responsible for managing the data of the interface , It should not access views or hold Activity and Fragment References to .
- ViewModels can also be used as a communication layer between different Fragments of an Activity. Each Fragment can acquire the ViewModel using the same key via their Activity. This allows communication between Fragments in a de-coupled fashion such that they never need to talk to the other Fragment directly.
ViewModel It can also be used as the same Activity Different from Fragment Communication layer between , Every Fragment Can pass Activity Use the same key To get ViewModel, This makes Fragment They communicate through decoupling .
1.2、ViewModel Basic use
- Customize ViewModel Inherit ViewMode, If ViewModel Required in Context Then inherit AndroidViewModel;
- stay Activity perhaps Fragment Pass through
val vm by viewModels<TVM>()
obtain ViewModel example .
2、ViewModel Related source code analysis
2.1、ViewModel Instance acquisition
val vm by viewModels<VM>()
What did you do ?
@MainThread
public inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
val factoryPromise = factoryProducer ?: {
defaultViewModelProviderFactory
}
return ViewModelLazy(VM::class, { viewModelStore }, factoryPromise)
}
Copy code
viewModels
yes ComponentActivity
An extension of , Pass in a Factory
Factory example , When not transmitted, it defaults to defaultViewModelProviderFactory
As the creation of ViewModel Our factory , return Lazy<VM>
type , Actually created a ViewModelLazy
Object and return .
It's used here Kotlin Property delegate for . Use reified and inline Inline the method to the caller , Generic parameters are replaced directly , Avoid passing Class Parameters of type .
2.1.1、Factory
androidx.lifecycle.ViewModelProvider.Factory, yes ViewModelProvider
Class , Used to create ViewModel example
public interface Factory {
<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}
Copy code
2.1.2、Lazy
Interface , Used to delay initialization
public interface Lazy<out T> {
public val value: T
public fun isInitialized(): Boolean
}
Copy code
2.1.3、ViewModelLazy
Attribute delegate corresponding class , Provide getValue and setValue Method ( When the property is var Only when setValue)
public class ViewModelLazy<VM : ViewModel> (
private val viewModelClass: KClass<VM>,
private val storeProducer: () -> ViewModelStore,
private val factoryProducer: () -> ViewModelProvider.Factory
) : Lazy<VM> {
private var cached: VM? = null
override val value: VM
get() {
val viewModel = cached
return if (viewModel == null) {
val factory = factoryProducer()
val store = storeProducer()
// adopt ViewModelProvider selection
ViewModelProvider(store, factory).get(viewModelClass.java).also {
cached = it
}
} else {
viewModel
}
}
override fun isInitialized(): Boolean = cached != null
}
Copy code
2.1.4、ViewModelProvider
It is equivalent to a tool class , from ViewModelStore Take out or generate the corresponding ViewModel.
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;
}
Copy code
2.2、ViewModel Destruction of
2.2.1、ViewModel Of clear Method
@MainThread
final void clear() {
mCleared = true;
if (mBagOfTags != null) {
synchronized (mBagOfTags) {
for (Object value : mBagOfTags.values()) {
// see comment for the similar call in setTagIfAbsent
closeWithRuntimeException(value);
}
}
}
onCleared();
}
// Close all dependencies ViewModel Of Closeable example
private static void closeWithRuntimeException(Object obj) {
if (obj instanceof Closeable) {
try {
((Closeable) obj).close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
// Provide users with their own ViewModel The time to release and clean up relevant resources
protected void onCleared() {
}
Copy code
ViewModel Of clear There are two main things done in the method :
- Traverse mBagOfTags This HashMap, Will the Closeable Object call of type close Method
- call onCleared Method to execute the cleaning logic of user expansion
among mBagOfTags
stay setTagIfAbsent
Method is filled with .
<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;
}
Copy code
adopt AS Of Find Usage Find the location where the method is called and find the following code :
val ViewModel.viewModelScope: CoroutineScope
get() {
val scope: CoroutineScope? = this.getTag(JOB_KEY)
if (scope != null) {
return scope
}
return setTagIfAbsent(JOB_KEY,
CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate))
}
internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
override val coroutineContext: CoroutineContext = context
override fun close() {
coroutineContext.cancel()
}
}
Copy code
This is a ViewModel ktx Expansion method in Library , You can see that we usually ViewModel Extended properties used in viewModelScope
The actual type is CloseableCoroutineScope, And its close Method cancels all Job, So we have ViewModel In the use of viewModelScope The start-up process will be in ViewModel Automatically cancel on destruction .
2.2.2、clear Method call timing
Using the same AS Of Find Usage Find the method in ViewModelStore Of clear Method is called , Look up further , Found in ComponentActivity Called ViewModelStore Of clear Method . In its construction method, there is the following code :
// Register an observer for a lifecycle event
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
mContextAwareHelper.clearAvailableContext();
// No configuration changes have occurred , Clean up all associated ViewModel
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
Copy code
2.3、Activity When the configuration changes ViewModel The recovery of
2.3.1、 preservation
When the configuration changes, the current... Will be destroyed first Activity Then rebuild ,Activity Destruction will come ActivityThread Medium performDestroyActivity
In the method , This method calls Activity Of retainNonConfigurationInstances
Method , And save the returned results to ActivityClientRecord In the object .
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
ActivityClientRecord r = mActivities.get(token);
Class<? extends Activity> activityClass = null;
if (r != null) {
activityClass = r.activity.getClass();
r.activity.mConfigChangeFlags |= configChanges;
if (finishing) {
r.activity.mFinished = true;
}
performPauseActivityIfNeeded(r, "destroy");
if (!r.stopped) {
callActivityOnStop(r, false /* saveState */, "destroy");
}
if (getNonConfigInstance) {
try {
// obtain NonConfigurationInstances
r.lastNonConfigurationInstances
= r.activity.retainNonConfigurationInstances();
} catch (Exception e) {
}
}
// ......
return r;
}
Copy code
Activity Of retainNonConfigurationInstances in , First, I call onRetainNonConfigurationInstance Method , The method in Activity Is an empty method , stay ComponentActivity The method is implemented in . And then created activityNonConfigurationInstances example , And will onRetainNonConfigurationInstance The return value of is assigned to the member activity.
NonConfigurationInstances retainNonConfigurationInstances() {
// 1、 First call onRetainNonConfigurationInstance
Object activity = onRetainNonConfigurationInstance();
HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
mFragments.doLoaderStart();
mFragments.doLoaderStop(true);
ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();
if (activity == null && children == null && fragments == null && loaders == null
&& mVoiceInteractor == null) {
return null;
}
// 2、 establish NonConfigurationInstances Object and assign values
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.activity = activity;
nci.children = children;
nci.fragments = fragments;
nci.loaders = loaders;
if (mVoiceInteractor != null) {
mVoiceInteractor.retainInstance();
nci.voiceInteractor = mVoiceInteractor;
}
return nci;
}
Copy code
ComponentActivity Will define their own internal NonConfigurationInstances, And will mViewModelStore Save the instance .
public final Object onRetainNonConfigurationInstance() {
// 1、 Customized saved content
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
viewModelStore = nc.viewModelStore;
}
}
if (viewModelStore == null && custom == null) {
return null;
}
// 2、 preservation viewModelStore
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
Copy code
Follow the call path , stay Activity At the time of destruction , Will ViewModelStore Save to NonConfigurationInstances in , And the instance will be saved in ActivityThread Of ActivityClientRecord in .
2.3.1、 recovery
From the preservation process , What we keep is ViewModelStore object , and ViewModel It is kept in ViewModelStore in ,ComponentActivity Realized ViewModelStoreOwner Interface , Its getViewModelStore
The method is to get ViewModelStore Of , Therefore, the process of reply should be implemented in this method .
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.");
}
ensureViewModelStore();
return mViewModelStore;
}
Copy code
Call directly ensureViewModelStore
Then return mViewModelStore
member , therefore ensureViewModelStore
It should be done in the method mViewModelStore
The initialization .
void ensureViewModelStore() {
if (mViewModelStore == null) {
// 1、 obtain NonConfigurationInstances (ComponentActivity Medium )
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance(); // The return is Object
if (nc != null) {
// 2、 Get the ViewModelStore
mViewModelStore = nc.viewModelStore;
}
// 3、 No access to , Corresponding to first creation , direct new One
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
}
Copy code
You can see through getLastNonConfigurationInstance Got it ComponentActivity Inside NonConfigurationInstances type , Then take... From it viewModelStore. Come here , We need to understand getLastNonConfigurationInstance
How to get the instance .
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
Copy code
getLastNonConfigurationInstance
yes Activity Methods in class , When mLastNonConfigurationInstances
Property is not null when , return activity Field (Object) type .mLastNonConfigurationInstances
yes Activity Medium NonConfigurationInstances type .
Search for Activity Code for , Find out mLastNonConfigurationInstances
Is in attach
Method . We know attach The method is in ActivityThread Of performLaunchActivity
Called in method .
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken);
Copy code
You can see that... Is passed directly when calling ActivityClientRecord
Of lastNonConfigurationInstances
. So here ActivityClientRecord Where did it come from . We know activity Of attach Is in ActivityThread Of performLaunchActivity Called in the method , So we can find the next breakpoint in this method , Find... By looking at the function call stack ActivityClientRecord Where did it come from .
- AS In the middle of Framework Source debugging
Because the code on the real machine is usually modified by the mobile phone manufacturer , Debugging may not correspond to , So here we choose to use in AS Create an emulator in to debug . For example, we need to debug ActivityThread Corresponding code , Rely on the SDK The version may be inconsistent with the version on the debugging machine . So we can download the corresponding mobile phone SDK Version of ActivityThread Source code , Then make sure ActivityThread Of package(android.app); Then create a file with the same name package, Then copy the code to the package Debugging can be carried out under .
I am Android-5.1 It is debugged on the simulator , Finally found in performRelaunchActivity Method through the following code ActivityClientRecord. Then pass it on to handleLaunchActivity Method , All the way to performLaunchActivity In the method , Finally, it was passed on attach In the method .
ActivityClientRecord r = mActivities.get(tmp.token);
// ......
handleLaunchActivity(r, currentIntent);
// mActivities The definition of
final ArrayMap<IBinder, ActivityClientRecord> mActivities
= new ArrayMap<IBinder, ActivityClientRecord>();
Copy code
You can see mActivities
yes ActivityThread A member variable of class , therefore ActivityClientRecord Is stored in the ActivityThread Medium , So as long as the application is not destroyed , This content will not be lost .( normal Destroy The corresponding... Should be removed from it ActivityClientRecord).
copyright notice
author[LiuShangYuan],Please bring the original link to reprint, thank you.
https://en.cdmana.com/2022/01/202201270350108125.html
The sidebar is recommended
- Spring IOC container loading process
- [thinking] the difference between singleton mode and static method - object-oriented programming
- Hadoop environment setup (MySQL environment configuration)
- 10 minutes, using node JS creates a real-time early warning system for bad weather!
- Git tool
- Force deduction algorithm - 92 Reverse linked list II
- What is the sub problem of dynamic programming?
- C / C + +: static keyword summary
- Idea does not have the artifacts option when configuring Tomcat
- Anaconda can't open it
guess what you like
-
I don't know how to start this
-
Matlab simulation of transportation optimization algorithm based on PSO
-
MySQL slow log optimization
-
[Vue] as the window is stretched (larger, smaller, wider and higher), the text will not be displayed
-
Popular Linux distributions for embedded computing
-
Suzhou computer research
-
After installing SSL Certificate in Windows + tomcat, the domain name request is not successful. Please answer!!
-
Implementation time output and greetings of jQuery instance
-
The 72 year old uncle became popular. Wu Jing and Guo fan made his story into a film, which made countless dreamers blush
-
How to save computer research
Random recommended
- Springboot implements excel import and export, which is easy to use, and poi can be thrown away
- The final examination subjects of a class are mathematical programming, and the scores are sorted and output from high to low
- Two pronged approach, Tsinghua Professor Pro code JDK and hotspot source code notes, one-time learning to understand
- C + + recursive knapsack problem
- The use of GIT and GitHub and the latest git tutorial are easy to understand -- Video notes of crazy God speaking
- PostgreSQL statement query
- Ignition database test
- Context didn't understand why he got a high salary?, Nginxfair principle
- Bootstrap switch switch control user's guide, springcloud actual combat video
- A list that contains only strings. What other search methods can be used except sequential search
- [matlab path planning] multi ant colony algorithm grid map path planning [including GUI source code 650]
- [matlab path planning] improved genetic algorithm grid map path planning [including source code phase 525]
- Iinternet network path management system
- Appium settings app is not running after 5000ms
- Reactnative foundation - 07 (background image, status bar, statusbar)
- Reactnative foundation - 04 (custom rpx)
- If you want an embedded database (H2, hsql or Derby), please put it on the classpath
- When using stm32g070 Hal library, if you want to write to flash, you must perform an erase. If you don't let it, you can't write continuously.
- Linux checks where the software is installed and what files are installed
- SQL statement fuzzy query and time interval filtering
- 69. Sqrt (x) (c + + problem solving version with vs runnable source program)
- Fresh students are about to graduate. Do you choose Java development or big data?
- Java project: OA management system (java + SSM + bootstrap + MySQL + JSP)
- Titanic passenger survival prediction
- Vectorization of deep learning formula
- Configuration and use of private image warehouse of microservice architect docker
- Relearn JavaScript events
- For someone, delete return 1 and return 0
- How does Java dynamically obtain what type of data is passed? It is used to judge whether the data is the same, dynamic data type
- How does the database cow optimize SQL?
- [data structure] chain structure of binary tree (pre order traversal) (middle order traversal) (post order traversal) (sequence traversal)
- Webpack packaging optimization solution
- 5. Operation element
- Detailed explanation of red and black trees
- redhat7. 9 install database 19C
- Blue Bridge Cup notes: (the given elements are not repeated) complete arrangement (arrangement cannot be repeated, arrangement can be repeated)
- Detailed explanation of springboot default package scanning mechanism and @ componentscan specified scanning path
- How to solve the run-time exception of test times
- Detailed explanation of k8s management tool kubectl
- Android system view memory command