Android Jetpack understands SavedStateHandle

jetpack library understands SavedStateHandle

Function

Cooperate with ViewModel to restore the related state data of ViewModel from the destruction and reconstruction of Activity. We know that when Activity is destroyed and rebuilt by the Android system, the onSaveInstanceState and onRestoreInstanceState methods will be called. This method is invisible to ViewModel. SavedStateHandle fills in Clear this gap so that ViewModel can save some state data through this

Description

SavedStateHandle is a key-value map used to retrieve and write data to the saved state through the set() and get() methods

How to use?

  1. Use SavedStateHandle as a construction parameter of ViewModel
  2. ViewModel internally generates a LiveData object through the SavedStateHandle.getLiveData method. The data in LiveData is the data we want to persist. If the Activity is rebuilt, this LiveData will is the reconstructed data

Supported types

Theoretically, all types that can be saved by Bundle are supported.

Principle analysis

  • SavedStateRegistryOwner

Used to indicate that this class has the ability to reconstruct data and is used to provide SavedStateRegistry

  • SavedStateRegistryController

Control class of SavedStateRegistry

  • SavedStateRegistry

Data storage and recovery

  • SavedStateProvider

Encapsulate the data to be saved through Bundle

basic relationship

SavedStateRegistryOwner → SavedStateRegistryController → SavedStateRegistry → SavedStateProvider

SavedStateRegistryController can be understood as a wrapper class of SavedStateRegistry

Code flow analysis

save data

When Activity calls onSaveInstanceState, the performSave method of SavedStateRegistryController is called. The method is as follows

@MainThread
    fun performSave(outBundle: Bundle) {<!-- -->
        savedStateRegistry.performSave(outBundle)
    }


Directly calling the performSave method of SavedStateRegistry

 @MainThread
    @Suppress("INACESSIBLE_TYPE")
    fun performSave(outBundle: Bundle) {<!-- -->
        val components = Bundle() // Create a Bundle
                //The state retained in the last rebuild is retained first
        if (restoredState != null) {<!-- -->
            components.putAll(restoredState)
        }
        val it: Iterator<Map.Entry<String, SavedStateProvider>> =
            this.components.iteratorWithAdditions()
        // As you can see, here we basically traverse the components, then call the saveState method and put it into the bundle we created.
        while (it.hasNext()) {<!-- -->
            val (key, value) = it.next()
            components.putBundle(key, value.saveState())
        }
                //Finally save this part of the data to outBundle
        if (!components.isEmpty) {<!-- -->
            outBundle.putBundle(SAVED_COMPONENTS_KEY, components)
        }
    }


Reconstruct data

The reconstructed data is reconstructed in onCreate

@OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {<!-- -->
        // Restore the Saved State first so that it is available to
        //OnContextAvailableListener instances
        mSavedStateRegistryController.performRestore(savedInstanceState);
        mContextAwareHelper.dispatchOnContextAvailable(this);
        super.onCreate(savedInstanceState);
        ReportFragment.injectIfNeededIn(this);
        if (BuildCompat.isAtLeastT()) {<!-- -->
            mOnBackPressedDispatcher.setOnBackInvokedDispatcher(
                    Api33Impl.getOnBackInvokedDispatcher(this)
            );
        }
        if (mContentLayoutId != 0) {<!-- -->
            setContentView(mContentLayoutId);
        }
    }


Controller’s performRestore method is called

@MainThread
    fun performRestore(savedState: Bundle?) {<!-- -->
        // To support backward compatibility with libraries that do not explicitly
        // call performAttach(), we make sure that work is done here
        if (!attached) {<!-- -->
            performAttach()
        }
        val lifecycle = owner.lifecycle
        check(!lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {<!-- -->
            ("performRestore cannot be called when owner is ${lifecycle.currentState}")
        }
        savedStateRegistry.performRestore(savedState)
    }


Finally, the performRestore method of SavedStateRegistry is called

internal fun performRestore(savedState: Bundle?) {<!-- -->
        check(attached) {<!-- -->
            ("You must call performAttach() before calling " +
                "performRestore(Bundle).")
        }
        check(!isRestored) {<!-- --> "SavedStateRegistry was already restored." }
        restoredState = savedState?.getBundle(SAVED_COMPONENTS_KEY)

        isRestored = true
    }


Serialize previously saved values into restoredState

The SavedStateRegistry mechanism realizes the saving of data. To use this mechanism, you need to implement SavedStateProvider, and then register yourself in it through the registerSavedStateProvider of SavedRegistry.

So how is this mechanism used by ViewModel? Activity and Fragment implement the HasDefaultViewModelProviderFactory interface, such as Activity

public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {<!-- -->
        if (mDefaultFactory == null) {<!-- -->
            mDefaultFactory = new SavedStateViewModelFactory(
                    getApplication(),
                    this,
                    getIntent() != null ? getIntent().getExtras() : null);
        }
        return mDefaultFactory;
    }


When SavedStateViewModelFactory creates ViewModel, if there is SaveStateHandle in the constructor of ViewModel, pass

LegacySavedStateHandleController.create(
            savedStateRegistry!!, lifecycle, key, defaultArgs
        )


Create SavedStateHandleController and obtain SavedStateHandle.

fun create(
        registry: SavedStateRegistry,
        lifecycle: lifecycle,
        key: String?,
        defaultArgs: Bundle?
    ): SavedStateHandleController {<!-- -->
                // Get the bundle related to this ViewModel from the registry
        val restoredState = registry.consumeRestoredStateForKey(key!!)
                // This method is to split the key and value saved in restoreState and save them as a map
        val handle = createHandle(restoredState, defaultArgs)
        val controller = SavedStateHandleController(key, handle)
        controller.attachToLifecycle(registry, lifecycle)
        tryToAddRecreator(registry, lifecycle)
        return controller
    }


Finally, the ViewModel was created successfully. At this time, the SavedHandle also has saved data. After the SavedStateHandleController is created, it will register the created SavedStateProvider into the StateRegistry to realize the saving of data.

Finally

If you want to become an architect or want to break through the 20-30K salary range, then don’t be limited to coding and business, you must be able to select and expand, and improve your programming thinking. In addition, good career planning is also very important, and learning habits are important, but the most important thing is to be able to persevere. Any plan that cannot be implemented consistently is empty talk.

If you have no direction, here is a set of “Advanced Notes on the Eight Modules of Android” written by a senior architect at Alibaba to help you systematically organize messy, scattered, and fragmented knowledge, so that you can systematically and efficiently Master various knowledge points of Android development.
img
Compared with the fragmented content we usually read, the knowledge points in this note are more systematic, easier to understand and remember, and are strictly arranged according to the knowledge system.

Everyone is welcome to support with three clicks. If you need information in the article, just scan the CSDN official certification WeChat card at the end of the article to get it for free ↓↓↓ (There is also a small bonus of the ChatGPT robot at the end of the article, don’t miss it)

PS: There is also a ChatGPT robot in the group, which can answer everyone’s work or technical questions

Picture