Why SavedStateHandle

There are two ways in which an Activity can be accidentally destroyed

  1. The resource configuration is changedActivityDestroy, such as screen rotation
  2. System resources are limitedActivityBe 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

SavedStateHandleThe use process of is generally as follows:

  1. willSavedStateHandleAs aViewModelThe structural parameters of
  2. ViewModelThrough internalSavedStateHandle.getLiveDataMethod to generate oneLiveDataObject,LiveDataIs the data we want to persist. If it is a fresh bootActivity.LiveDataThe value saved innull; If it’s reconstructedActivity.LiveDataThe value saved in the
  3. To pass togetLiveDatamethodsStringThe argument is a uniqueKeyAnd finally save toBundleThe key-value pair inKeyIn order toLiveDataValue 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 theParcelableclass

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:

SavedStateHandleThe working principle of is summarized as follows:

  1. Data preservationIs in theActivitytheonSaveInstanceState()Call theSavedStateRegistryControllertheperformSave()Method to implement
  2. SavedStateRegistryControllerisSavedStateRegistryData storage and recovery are forwarded to this class.performSave()Methods are eventually referred toSavedStateRegistrytheperformSave()In the.
  3. performSave()Write the data that needs to be saved toActivitytheBundleObject implementation
  4. Data recoveryIn theonCreate()Call theperformRestore()Method to retrieve the saved data for recovery
  5. For data that needs to be saved, implementSavedStateProviderInterface, register the need to save the data; Fetch data; External by use and passregisterSavedStateProvider()The method is the samekeyTo retrieve the data, and after retrieving the data frommRestoredStateRemoved.
  6. ViewModelThis is implemented by default when it is createdSavedStateProviderAnd so on the interface, the realization of data saving fromViewModelTo obtain the data, to restoreViewModelThe assignment.
  7. 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
  1. ComponentActivityTo achieve theSavedStateRegistryOwnerInterface, throughSavedStateRegistryControllerTo create aSavedStateRegistryobject
  2. onCreate()Method is calledSavedStateRegistryControllertheperformRestore()Method to restore saved data
  3. onDestroy()Method is calledSavedStateRegistryControllertheperformSave()Method to save data.

SavedStateRegistryControllerclass

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.

SavedStateRegistryclass

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 () :

  1. Determine whether the data recovered last time exists. If so, add and save the data.
  2. Save everything you needBundleKeep;
  3. Writes all saved data toActivitytheBundleobject

Summary:

  1. Data is saved inActivitytheonSaveInstanceState()Call theSavedStateRegistryControllertheperformSave()Method to implement
  2. SavedStateRegistryControllerisSavedStateRegistryData storage and recovery are forwarded to this class.performSave()Methods are eventually referred toSavedStateRegistrytheperformSave()In the.
  3. performSave()Write the data that needs to be saved toActivitytheBundleObject 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 fromViewModelTo get the data? How to restore dataViewModelThe 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:

  1. Determine what needs to be createdViewModelWhether it isAndroidViewModelA subclass
  2. judgeViewModelHave parameters or notSavedStateHandleConstructor of. If not, useAndroidViewModelFactorycreateviewmodelInstance; If so, buildSavedStateHandleControllerobject
  3. Reflection structureViewModelInstance object.

SavedStateHandleControllerclass

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:

  1. Get data cache;
  2. buildSavedStateHandleObject to split and store the data cache;
  3. buildSavedStateHandleControllerThe object,SavedStateHandleThe data that needs to be saved is registeredSavedStateRegistryIn the.

Among them:

  1. SavedStateHandleControllerIn thekeyisViewModelthekey;
  2. defaultArgsforActivityisgetIntent().getExtras()forFragmentisgetArguments()

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

  1. initialStateIs stored inActivityThe key-value pair data retained during reconstruction
  2. mRegularIs 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

SavingStateLiveDataclass

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

setSavedStateProvidermethods

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:

  1. Data preservationIs in theActivitytheonSaveInstanceState()Call theSavedStateRegistryControllertheperformSave()Method to implement
  2. SavedStateRegistryControllerisSavedStateRegistryData storage and recovery are forwarded to this class.performSave()Methods are eventually referred toSavedStateRegistrytheperformSave()In the.
  3. performSave()Write the data that needs to be saved toActivitytheBundleObject implementation
  4. Data recoveryIn theonCreate()Call theperformRestore()Method to retrieve the saved data for recovery
  5. For data that needs to be saved, implementSavedStateProviderInterface, register the need to save the data; Fetch data; External by use and passregisterSavedStateProvider()The method is the samekeyTo retrieve the data, and after retrieving the data frommRestoredStateRemoved.
  6. ViewModelThis is implemented by default when it is createdSavedStateProviderAnd so on the interface, the realization of data saving fromViewModelTo obtain the data, to restoreViewModelThe assignment.
  7. The data stored cannot exceed 1 MB