Why SavedStateHandle
There are two ways in which an Activity can be accidentally destroyed
- The resource configuration is changed
Activity
Destroy, such as screen rotation - System resources are limited
Activity
Be destroyed
The ViewModel can solve the data recovery problem in the first case, but for the second case, you need to rely on the data protection and recovery mechanism provided natively, namely onSaveInstanceState(Bundle) and onRestoreInstanceState(Bundle).
The data saved by the onSaveInstanceState(Bundle) method is retained when the resource configuration changes or the Activity is killed unexpectedly, but there are storage capacity and access speed limitations. The onSaveInstanceState(Bundle) method serializes data to disk. Serialization can consume a lot of memory and time if the data to be stored is complex.
So onSaveInstanceState(Bundle) is only suitable for storing a small number of simple types of data
In the second destruction case of an Activity, the saving and recovery of data is limited to the Activity’s specific methods, and there is no way to decide directly in the ViewModel what data needs to be kept, nor to retrieve the restored data directly.
The SavedStateHandle component can be seen as an extension of the ViewModel functionality to address the problem that the ViewModel cannot sense when onSaveInstanceState is triggered
Two, basic use
2.1 Simple Use
SavedStateHandle
The use process of is generally as follows:
- will
SavedStateHandle
As aViewModel
The structural parameters of ViewModel
Through internalSavedStateHandle.getLiveData
Method to generate oneLiveData
Object,LiveData
Is the data we want to persist. If it is a fresh bootActivity
.LiveData
The value saved innull
; If it’s reconstructedActivity
.LiveData
The value saved in the- To pass to
getLiveData
methodsString
The argument is a uniqueKey
And finally save toBundle
The key-value pair inKey
In order toLiveData
Value as thevalue
Take a look at the SavedStateHandle example demonstration
Take SavedStateHandle as the ViewModel construction parameter
class SavedStateViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
companion object {
private const val KEY_NAME = "keyName"
}
/ / by SavedStateHandle getLiveData method to generate a LiveData object
val nameLiveData = savedStateHandle.getLiveData<String>(KEY_NAME)
val blogLiveData = MutableLiveData<String>()
}
Copy the code
Use the ViewModel
class MainActivity : AppCompatActivity() {
private val savedStateViewModel by lazy {
ViewModelProvider(this).get(SavedStateViewModel::class.java)
}
override fun onCreate(savedInstanceState: Bundle?). {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
log("savedStateViewModel: $savedStateViewModel")
log("savedStateViewModel.name: ${savedStateViewModel.nameLiveData.value}")
log("savedStateViewModel.blog: ${savedStateViewModel.blogLiveData.value}")
log("onCreate")
btn_test.setOnClickListener {
savedStateViewModel.nameLiveData.value = "SavedStateViewModel"
savedStateViewModel.blogLiveData.value = "https://https://zhewendev.github.io/"}}private fun log(log: String) {
Log.e("MainActivity", log)
}
}
Copy the code
// Print the result
/**MainActivity starts for the first time **/
E/MainActivity: savedStateViewModel: com.zhewen.navigationcodelab.navigation.SavedStateViewModel@53e1b17
E/MainActivity: savedStateViewModel.name: null
E/MainActivity: savedStateViewModel.blog: null
E/MainActivity: onCreate
/** button After clicking assign, press the Home button to exit and re-enter */
E/MainActivity: savedStateViewModel: com.zhewen.navigationcodelab.navigation.SavedStateViewModel@b613350
E/MainActivity: savedStateViewModel.name: SavedStateViewModel
E/MainActivity: savedStateViewModel.blog: null
E/MainActivity: onCreate
Copy the code
You can simulate an Activity being destroyed due to lack of system memory by turning on the “Do not keep Activity” option in developer mode
As you can see from the log print, the ViewModel has been destroyed and rebuilt, but the nameLiveData built with SavedStateHandle still has the previous values
SavedStateHandle is implemented by encapsulating onSaveInstanceState(Bundle) and onCreate(Bundle)
SavedStateHandle stores the data in the Bundle through the onSaveInstanceState(Bundle) method when the Activity is destroyed, and removes the data from onCreate(Bundle?) when it is rebuilt. The developer is only responsible for storing and retrieving data from SavedStateHandle and does not need to interact with the Activity directly, thus simplifying the whole development process
2.2 Supported Types
Data retained in SavedStateHandle is saved and restored as a Bundle along with the rest of the Activity or Fragment’s onSaveInstanceState(Bundle).
Directly supported types
By default, you can call set() and get() on SavedStateHandle to handle the same data types as the Bundle
Type/class support | An array of support |
---|---|
double |
double[] |
int |
int[] |
long |
long[] |
String |
String[] |
byte |
byte[] |
char |
char[] |
CharSequence |
CharSequence[] |
float |
float[] |
Parcelable |
Parcelable[] |
Serializable |
Serializable[] |
short |
short[] |
SparseArray |
|
Binder |
|
Bundle |
|
ArrayList |
|
Size (only in API 21+) |
|
SizeF (only in API 21+) |
If the class does not extend any of the above lists, consider making the class Parcelable by annotating @Parcelize Kotlin or simply implementing Parcelable.
Save theParcelable
class
If a class does not implement Parcelable or Serializable and cannot be modified to implement one of these interfaces, you cannot directly save an instance of that class into SavedStateHandle.
Lifecycle 2.0.0- Alpha03 begins, SavedStateHandle allows saving any object.
How to do it: Use the setSavedStateProvider() method to provide your own logic for saving and restoring objects as bundles
Take a look at the official example
class TempFileViewModel : ViewModel() {
private var tempFile: File? = null
fun createOrGetTempFile(a): File {
returntempFile ? : File.createTempFile("temp".null).also {
tempFile = it
}
}
}
Copy the code
To ensure that temporary files are not lost if the Activity’s process terminates and then resumes, the TempFileViewModel can use SavedStateHandle to retain their data
private fun File.saveTempFile(a) = bundleOf("path", absolutePath)
class TempFileViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
private var tempFile: File? = null
init {
savedStateHandle.setSavedStateProvider("temp_file") { // saveState()
if(tempFile ! =null) {
tempFile.saveTempFile()
} else {
Bundle()
}
}
}
fun createOrGetTempFile(a): File {
returntempFile ? : File.createTempFile("temp".null).also {
tempFile = it
}
}
}
Copy the code
To restore File data when the user returns, retrieve the Temp_file Bundle from SavedStateHandle. This is exactly the Bundle containing the absolute path provided by saveTempFile(). This absolute path can then be used to instantiate a new File.
private fun File.saveTempFile(a) = bundleOf("path", absolutePath)
private fun Bundle.restoreTempFile(a) = if (containsKey("path")) {
File(getString("path"))}else {
null
}
class TempFileViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
private var tempFile: File? = null
init {
val tempFileBundle = savedStateHandle.get<Bundle>("temp_file")
if(tempFileBundle ! =null) {
tempFile = tempFileBundle.restoreTempFile()
}
savedStateHandle.setSavedStateProvider("temp_file") { // saveState()
if(tempFile ! =null) {
tempFile.saveTempFile()
} else {
Bundle()
}
}
}
fun createOrGetTempFile(a): File {
returntempFile ? : File.createTempFile("temp".null).also {
tempFile = it
}
}
}
Copy the code
Three, principle analysis
Let’s take a look at its core roles:
-
SavedStateRegistryOwner
An interface that marks the Activity/Fragment class that has the ability to recreate data by providing SavedStateRegistry objects. The main implementation classes of this interface are Activity and Fragment.
-
SavedStateRegistryController
SavedStateRegistry control class, which creates SavedStatedRegistry, which connects activities/fragments with SavedStateRegistry;
-
SavedStateRegistry
Data storage and recovery center class
-
SaveStateProvider
Provides saved and restored data. The interface has only one saveState method, which bundles the data that needs to be saved.
-
.
For the relationship between these core roles, see the following figure:
SavedStateHandle
The working principle of is summarized as follows:
- Data preservationIs in the
Activity
theonSaveInstanceState()
Call theSavedStateRegistryController
theperformSave()
Method to implement SavedStateRegistryController
isSavedStateRegistry
Data storage and recovery are forwarded to this class.performSave()
Methods are eventually referred toSavedStateRegistry
theperformSave()
In the.performSave()
Write the data that needs to be saved toActivity
theBundle
Object implementation- Data recoveryIn the
onCreate()
Call theperformRestore()
Method to retrieve the saved data for recovery - For data that needs to be saved, implement
SavedStateProvider
Interface, register the need to save the data; Fetch data; External by use and passregisterSavedStateProvider()
The method is the samekey
To retrieve the data, and after retrieving the data frommRestoredState
Removed. ViewModel
This is implemented by default when it is createdSavedStateProvider
And so on the interface, the realization of data saving fromViewModel
To obtain the data, to restoreViewModel
The assignment.- The data stored cannot exceed 1 MB
3.1 Data Storage
SavedStateRegistryOwner provides a SavedStateRegistry object whose implementation class has data reconstruction capabilities
public interface SavedStateRegistryOwner extends LifecycleOwner {
/**
* Returns owned {@link SavedStateRegistry}
*/
@NonNull
SavedStateRegistry getSavedStateRegistry(a);
}
Copy the code
For an example of an Activity, look at the ComponentActivity class
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
ContextAware.LifecycleOwner.ViewModelStoreOwner.HasDefaultViewModelProviderFactory.SavedStateRegistryOwner.OnBackPressedDispatcherOwner.ActivityResultRegistryOwner.ActivityResultCaller {
final SavedStateRegistryController mSavedStateRegistryController =
SavedStateRegistryController.create(this);
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) { mSavedStateRegistryController.performRestore(savedInstanceState); . }@CallSuper
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {... mSavedStateRegistryController.performSave(outState); mActivityResultRegistry.onSaveInstanceState(outState); }@NonNull
@Override
public final SavedStateRegistry getSavedStateRegistry(a) {
returnmSavedStateRegistryController.getSavedStateRegistry(); }... }Copy the code
ComponentActivity
To achieve theSavedStateRegistryOwner
Interface, throughSavedStateRegistryController
To create aSavedStateRegistry
objectonCreate()
Method is calledSavedStateRegistryController
theperformRestore()
Method to restore saved dataonDestroy()
Method is calledSavedStateRegistryController
theperformSave()
Method to save data.
SavedStateRegistryController
class
SavedStateRegistryController is SavedStateRegistry control class, data saving and restoring are forwarded to the class.
Take a look at the internal implementation.
public final class SavedStateRegistryController {
private final SavedStateRegistryOwner mOwner;
private final SavedStateRegistry mRegistry;
private SavedStateRegistryController(SavedStateRegistryOwner owner) {
mOwner = owner;
mRegistry = new SavedStateRegistry();
}
@NonNull
public SavedStateRegistry getSavedStateRegistry(a) {
return mRegistry;
}
@MainThread
public void performRestore(@Nullable Bundle savedState) {
Lifecycle lifecycle = mOwner.getLifecycle();
// Data recovery must be performed before the Activity's onCreate method call ends
if(lifecycle.getCurrentState() ! = Lifecycle.State.INITIALIZED) {throw new IllegalStateException("Restarter must be created only during "
+ "owner's initialization stage");
}
lifecycle.addObserver(new Recreator(mOwner));
mRegistry.performRestore(lifecycle, savedState);
}
@MainThread
public void performSave(@NonNull Bundle outBundle) {
mRegistry.performSave(outBundle);
}
@NonNull
public static SavedStateRegistryController create(@NonNull SavedStateRegistryOwner owner) {
return newSavedStateRegistryController(owner); }}Copy the code
SavedStateRegistryController will transfer the data save and restore to SavedStateRegistry method implementations of the same name.
Data is saved in the Activity of onSaveInstanceState () calls the SavedStateRegistryController performSave () method, Finally, it goes to performSave() in SavedStateRegistry.
SavedStateRegistry
class
Look at the performSave() implementation of the SavedStateRegistry class
public final class SavedStateRegistry {
private static final String SAVED_COMPONENTS_KEY =
"androidx.lifecycle.BundlableSavedStateRegistry.key";
private SafeIterableMap<String, SavedStateProvider> mComponents =
new SafeIterableMap<>();
@Nullable
private Bundle mRestoredState;
private boolean mRestored;
@MainThread
void performSave(@NonNull Bundle outBundle) {
Bundle components = new Bundle();
//mRestoredState is not empty, indicating that the restored data has not been consumed and the unconsumed data needs to be saved again.
if(mRestoredState ! =null) {
components.putAll(mRestoredState);
}
// Iterate over all registered SavedStateProviders to save the data provided by all SavedStateProviders. That is, all the bundles that need to be saved are saved by outBundle
for (Iterator<Map.Entry<String, SavedStateProvider>> it =
mComponents.iteratorWithAdditions(); it.hasNext(); ) {
Map.Entry<String, SavedStateProvider> entry1 = it.next();
components.putBundle(entry1.getKey(), entry1.getValue().saveState());
}
// Write all saved data to the Activity's BundleoutBundle.putBundle(SAVED_COMPONENTS_KEY, components); }...public interface SavedStateProvider {
@NonNull
Bundle saveState(a);// Bundle the data that needs to be saved}}Copy the code
PerformSave () :
- Determine whether the data recovered last time exists. If so, add and save the data.
- Save everything you need
Bundle
Keep; - Writes all saved data to
Activity
theBundle
object
Summary:
- Data is saved in
Activity
theonSaveInstanceState()
Call theSavedStateRegistryController
theperformSave()
Method to implement SavedStateRegistryController
isSavedStateRegistry
Data storage and recovery are forwarded to this class.performSave()
Methods are eventually referred toSavedStateRegistry
theperformSave()
In the.performSave()
Write the data that needs to be saved toActivity
theBundle
Object implementation
3.2 Data Recovery
For data recovery, look directly at SavedStateRegistry’s performRestore()
@MainThread
void performRestore(@NonNull Lifecycle lifecycle, @Nullable Bundle savedState) {
// It can be restored only once
if (mRestored) {
throw new IllegalStateException("SavedStateRegistry was already restored.");
}
// The data to be restored is not empty
if(savedState ! =null) {
mRestoredState = savedState.getBundle(SAVED_COMPONENTS_KEY);
}
// Lifecycle is monitored to determine whether the data is currently in a Lifecycle phase that can be recovered, marked by a Boolean variable mAllowingSavingState
lifecycle.addObserver(new GenericLifecycleObserver() {
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_START) {
mAllowingSavingState = true;
} else if (event == Lifecycle.Event.ON_STOP) {
mAllowingSavingState = false; }}});// The tag has been restored
mRestored = true;
}
Copy the code
Summary:
To restore data, the performRestore() method is called on onCreate() to retrieve the saved data and restore it
3.3 Data entry and consumption
How is data saved fromViewModel
To get the data? How to restore dataViewModel
The assignment?
Data storage entry
To save data, you need to add data to SafeIterableMap
mComponents. RegisterSavedStateProvider SavedStateRegistry provides an entry method ().
The external implementation of the SavedStateProvider interface in saveState() returns the data you want to save, Then call registerSavedStateProvider method will be submitted to SavedStateRegistry SavedStateProvider object.
@MainThread
public void registerSavedStateProvider(@NonNull String key,
@NonNull SavedStateProvider provider) {
// Register the data you want to save and wait until performSave. Externally, a unique identification key corresponds to the Bundle of data to be saved,
SavedStateProvider previous = mComponents.putIfAbsent(key, provider);
if(previous ! =null) {
throw new IllegalArgumentException("SavedStateProvider with the given key is"
+ " already registered"); }}Copy the code
To save data through SavedStateRegistry, simply implement the SavedStateProvider interface and register it. When you fetch the data, you just fetch it with the old key.
Data consumption entry
Data consumption, which requires developers to consume data by fetching key-value pairs to restore user data or UI state to the state before destruction.
Consumption data entry is consumeRestoredStateForKey () method
@MainThread
@Nullable
public Bundle consumeRestoredStateForKey(@NonNull String key) {
// Data is not recovered
if(! mRestored) {throw new IllegalStateException("You can consumeRestoredStateForKey "
+ "only after super.onCreate of corresponding component");
}
// The data to be restored is not empty
if(mRestoredState ! =null) {
Bundle result = mRestoredState.getBundle(key);
mRestoredState.remove(key);
if (mRestoredState.isEmpty()) {
mRestoredState = null;
}
return result;
}
return null;
}
Copy the code
External through the use of and to registerSavedStateProvider () method as the key to collect data, and the data after data acquisition from mRestoredState removed.
If all data has been consumed, set mRestoredState to NULL, indicating that all data has been consumed
3.4 Entry function calls
Have a look at where the invoked the registerSavedStateProvider () and consumeRestoredStateForKey ()
The Activity and the Fragment default implementation HasDefaultViewModelProviderFactory interface, the default return SavedStateViewModelFactory, have a look at its internal how to create ViewModel instance.
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
// Check whether viewModel is a subclass of AndroidViewModel
boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass);
Constructor<T> constructor;
if(isAndroidViewModel && mApplication ! =null) {
/ / is a subclass of AndroidViewModel, try to find whether it has two parameters (Application, SavedStateHandle) constructor
constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE);
} else {
// Find if it has a constructor for (SavedStateHandle) as an argument
constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE);
}
// doesn't need SavedStateHandle
if (constructor == null) {
// Create an instance of viewModel using AndroidViewModelFactory. It can have no arguments, or it can carry an Application argument
return mFactory.create(modelClass);
}
/ / build SavedStateHandleController object
SavedStateHandleController controller = SavedStateHandleController.create(
mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
try {
T viewmodel;
if(isAndroidViewModel && mApplication ! =null) {
// Reflection constructs the ViewModel instance object, passing the parameters along with it.
viewmodel = constructor.newInstance(mApplication, controller.getHandle());
} else {
viewmodel = constructor.newInstance(controller.getHandle());
}
viewmodel.setTagIfAbsent(TAG_SAVED_STATE_HANDLE_CONTROLLER, controller);
// Returns the newly created ViewModel instance object
returnviewmodel; }... }Copy the code
SavedStateViewModelFactory the create () method is the main content as follows:
- Determine what needs to be created
ViewModel
Whether it isAndroidViewModel
A subclass - judge
ViewModel
Have parameters or notSavedStateHandle
Constructor of. If not, useAndroidViewModelFactory
createviewmodel
Instance; If so, buildSavedStateHandleController
object - Reflection structure
ViewModel
Instance object.
SavedStateHandleController
class
See SavedStateHandleController the create () method
static SavedStateHandleController create(SavedStateRegistry registry, Lifecycle lifecycle, String key, Bundle defaultArgs) {
// Get the Bundle data object of the ViewModel from the data center SavedStateRegistry
Bundle restoredState = registry.consumeRestoredStateForKey(key);
// Build the SavedStateHandle object and split the restoredState data into mRegular
SavedStateHandle handle = SavedStateHandle.createHandle(restoredState, defaultArgs);
/ / build SavedStateHandleController object
SavedStateHandleController controller = new SavedStateHandleController(key, handle);
// register the data that needs to be saved in SavedStateHandle into SavedStateRegistry
controller.attachToLifecycle(registry, lifecycle);
tryToAddRecreator(registry, lifecycle);
return controller;
}
Copy the code
void attachToLifecycle(SavedStateRegistry registry, Lifecycle lifecycle) {
if (mIsAttached) {
throw new IllegalStateException("Already attached to lifecycleOwner");
}
mIsAttached = true;
lifecycle.addObserver(this);
registry.registerSavedStateProvider(mKey, mHandle.savedStateProvider());
}
Copy the code
The main content of SavedStateHandleController the create () method:
- Get data cache;
- build
SavedStateHandle
Object to split and store the data cache; - build
SavedStateHandleController
The object,SavedStateHandle
The data that needs to be saved is registeredSavedStateRegistry
In the.
Among them:
SavedStateHandleController
In thekey
isViewModel
thekey
;defaultArgs
forActivity
isgetIntent().getExtras()
forFragment
isgetArguments()
3.5 getLiveData
The foregoing example by SavedStateHandle. GetLiveData method to generate a LiveData object to store persistent store of data.
Take a look at the SavedStateHandle class
public final class SavedStateHandle {
final Map<String, Object> mRegular;
final Map<String, SavedStateProvider> mSavedStateProviders = new HashMap<>();
private finalMap<String, SavingStateLiveData<? >> mLiveDatas =new HashMap<>();
public SavedStateHandle(@NonNull Map<String, Object> initialState) {
mRegular = new HashMap<>(initialState);
}
public SavedStateHandle(a) {
mRegular = new HashMap<>();
}
@MainThread
public boolean contains(@NonNull String key) {
return mRegular.containsKey(key);
}
@MainThread
@NonNull
public <T> MutableLiveData<T> getLiveData(@NonNull String key) {
return getLiveDataInternal(key, false.null);
}
@MainThread
@Nullable
public <T> T get(@NonNull String key) {
return(T) mRegular.get(key); }... }Copy the code
SavedStateHandle contains two constructors
initialState
Is stored inActivity
The key-value pair data retained during reconstructionmRegular
Is the key/value pair data that will eventually be persisted
If the parameter constructor is called, it means that the initialization is the result of the Activity being destroyed and rebuilt. If the constructor is called with no arguments, this means that the initialization is a fresh start of the Activity
Take a look at the getLiveData method in SavedStateHandle
@MainThread
@NonNull
public <T> MutableLiveData<T> getLiveData(@NonNull String key) {
return getLiveDataInternal(key, false.null);
}
@MainThread
@NonNull
public <T> MutableLiveData<T> getLiveData(@NonNull String key,
@SuppressLint("UnknownNullness") T initialValue) {
return getLiveDataInternal(key, true, initialValue);
}
Copy the code
@NonNull
private <T> MutableLiveData<T> getLiveDataInternal(
@NonNull String key,
boolean hasInitialValue,
@Nullable T initialValue) {
MutableLiveData<T> liveData = (MutableLiveData<T>) mLiveDatas.get(key);
if(liveData ! =null) {
return liveData;
}
SavingStateLiveData<T> mutableLd;
// double hashing but null is valid value
if (mRegular.containsKey(key)) {
mutableLd = new SavingStateLiveData<>(this, key, (T) mRegular.get(key));
} else if (hasInitialValue) {
mutableLd = new SavingStateLiveData<>(this, key, initialValue);
} else {
mutableLd = new SavingStateLiveData<>(this, key);
}
mLiveDatas.put(key, mutableLd);
return mutableLd;
}
Copy the code
The main content of the getLiveData() method:
Return a LiveData object associated with key and mRegular. The initial default value of the LiveData object is chosen between mRegular and initialValue, and each generated LiveData object is saved in mLiveDatas for subsequent reuse
SavingStateLiveData
class
When an external value update operation is performed on LiveData, SavedStateHandle needs to get the latest value, because the final persisted value must also be the latest value.
The SavingStateLiveData object returned by the getLiveDataInternal method synchronizes the key-value pair data in mRegular after the setValue method is called.
static class SavingStateLiveData<T> extends MutableLiveData<T> {
private String mKey;
private SavedStateHandle mHandle;
@Override
public void setValue(T value) {
if(mHandle ! =null) {
// Update the key/value pair in mRegular
mHandle.mRegular.put(mKey, value);
}
super.setValue(value);
}
void detach(a) {
mHandle = null; }... }Copy the code
setSavedStateProvider
methods
SavedStateHandle opens a setSavedStateProvider method to the outside world to pass in the SavedStateProvider object, which is responsible for implementing the saveState() method to return the Bundle object that you want to persist the cache. SavedStateHandle is responsible for calling this method
@MainThread
public void setSavedStateProvider(@NonNull String key, @NonNull SavedStateProvider provider) {
mSavedStateProviders.put(key, provider);
}
Copy the code
private final SavedStateProvider mSavedStateProvider = new SavedStateProvider() {
@SuppressWarnings("unchecked")
@NonNull
@Override
public Bundle saveState(a) {
// Get the saved state from each SavedStateProvider registered with this
// SavedStateHandle, iterating through a copy to avoid re-entrance
Map<String, SavedStateProvider> map = new HashMap<>(mSavedStateProviders);
for (Map.Entry<String, SavedStateProvider> entry : map.entrySet()) {
Bundle savedState = entry.getValue().saveState();
set(entry.getKey(), savedState);
}
// Convert the Map of current values into a Bundle
Set<String> keySet = mRegular.keySet();
ArrayList keys = new ArrayList(keySet.size());
ArrayList value = new ArrayList(keys.size());
for (String key : keySet) {
keys.add(key);
value.add(mRegular.get(key));
}
Bundle res = new Bundle();
// "parcelable" arraylists - lol
res.putParcelableArrayList("keys", keys);
res.putParcelableArrayList("values", value);
returnres; }};Copy the code
Principle summary of SavedStateHandle:
- Data preservationIs in the
Activity
theonSaveInstanceState()
Call theSavedStateRegistryController
theperformSave()
Method to implement SavedStateRegistryController
isSavedStateRegistry
Data storage and recovery are forwarded to this class.performSave()
Methods are eventually referred toSavedStateRegistry
theperformSave()
In the.performSave()
Write the data that needs to be saved toActivity
theBundle
Object implementation- Data recoveryIn the
onCreate()
Call theperformRestore()
Method to retrieve the saved data for recovery - For data that needs to be saved, implement
SavedStateProvider
Interface, register the need to save the data; Fetch data; External by use and passregisterSavedStateProvider()
The method is the samekey
To retrieve the data, and after retrieving the data frommRestoredState
Removed. ViewModel
This is implemented by default when it is createdSavedStateProvider
And so on the interface, the realization of data saving fromViewModel
To obtain the data, to restoreViewModel
The assignment.- The data stored cannot exceed 1 MB