1 /*
<lambda>null2 * Copyright 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 @file:JvmName("SavedStateHandleSupport")
18
19 package androidx.lifecycle
20
21 import androidx.annotation.MainThread
22 import androidx.lifecycle.ViewModelProvider.Companion.VIEW_MODEL_KEY
23 import androidx.lifecycle.viewmodel.CreationExtras
24 import androidx.savedstate.SavedState
25 import androidx.savedstate.SavedStateRegistry
26 import androidx.savedstate.SavedStateRegistryOwner
27 import androidx.savedstate.read
28 import androidx.savedstate.savedState
29 import androidx.savedstate.write
30 import kotlin.jvm.JvmField
31 import kotlin.jvm.JvmName
32 import kotlin.reflect.KClass
33
34 internal const val VIEWMODEL_KEY = "androidx.lifecycle.internal.SavedStateHandlesVM"
35 internal const val SAVED_STATE_KEY = "androidx.lifecycle.internal.SavedStateHandlesProvider"
36
37 /**
38 * Enables the support of [SavedStateHandle] in a component.
39 *
40 * After this method, [createSavedStateHandle] can be called on [CreationExtras] containing this
41 * [SavedStateRegistryOwner] / [ViewModelStoreOwner].
42 *
43 * Must be called while component is in `INITIALIZED` or `CREATED` state and before a [ViewModel]
44 * with [SavedStateHandle] is requested.
45 */
46 @MainThread
47 public fun <T> T.enableSavedStateHandles() where
48 T : SavedStateRegistryOwner,
49 T : ViewModelStoreOwner {
50 val currentState = lifecycle.currentState
51 require(currentState == Lifecycle.State.INITIALIZED || currentState == Lifecycle.State.CREATED)
52
53 // Add the SavedStateProvider used to save SavedStateHandles
54 // if we haven't already registered the provider
55 if (savedStateRegistry.getSavedStateProvider(SAVED_STATE_KEY) == null) {
56 val provider = SavedStateHandlesProvider(savedStateRegistry, this)
57 savedStateRegistry.registerSavedStateProvider(SAVED_STATE_KEY, provider)
58 lifecycle.addObserver(SavedStateHandleAttacher(provider))
59 }
60 }
61
createSavedStateHandlenull62 private fun createSavedStateHandle(
63 savedStateRegistryOwner: SavedStateRegistryOwner,
64 viewModelStoreOwner: ViewModelStoreOwner,
65 key: String,
66 defaultArgs: SavedState?
67 ): SavedStateHandle {
68 val provider = savedStateRegistryOwner.savedStateHandlesProvider
69 val viewModel = viewModelStoreOwner.savedStateHandlesVM
70 // If we already have a reference to a previously created SavedStateHandle
71 // for a given key stored in our ViewModel, use that. Otherwise, create
72 // a new SavedStateHandle, providing it any restored state we might have saved
73 return viewModel.handles[key]
74 ?: SavedStateHandle.createHandle(provider.consumeRestoredStateForKey(key), defaultArgs)
75 .also { viewModel.handles[key] = it }
76 }
77
78 /**
79 * Creates `SavedStateHandle` that can be used in your ViewModels
80 *
81 * This function requires [enableSavedStateHandles] call during the component initialization. Latest
82 * versions of androidx components like `ComponentActivity`, `Fragment`, `NavBackStackEntry` makes
83 * this call automatically.
84 *
85 * This [CreationExtras] must contain [SAVED_STATE_REGISTRY_OWNER_KEY], [VIEW_MODEL_STORE_OWNER_KEY]
86 * and [VIEW_MODEL_KEY].
87 *
88 * @throws IllegalArgumentException if this `CreationExtras` are missing required keys:
89 * `ViewModelStoreOwnerKey`, `SavedStateRegistryOwnerKey`, `VIEW_MODEL_KEY`
90 */
91 @MainThread
createSavedStateHandlenull92 public fun CreationExtras.createSavedStateHandle(): SavedStateHandle {
93 val savedStateRegistryOwner =
94 this[SAVED_STATE_REGISTRY_OWNER_KEY]
95 ?: throw IllegalArgumentException(
96 "CreationExtras must have a value by `SAVED_STATE_REGISTRY_OWNER_KEY`"
97 )
98 val viewModelStateRegistryOwner =
99 this[VIEW_MODEL_STORE_OWNER_KEY]
100 ?: throw IllegalArgumentException(
101 "CreationExtras must have a value by `VIEW_MODEL_STORE_OWNER_KEY`"
102 )
103
104 val defaultArgs = this[DEFAULT_ARGS_KEY]
105 val key =
106 this[VIEW_MODEL_KEY]
107 ?: throw IllegalArgumentException(
108 "CreationExtras must have a value by `VIEW_MODEL_KEY`"
109 )
110 return createSavedStateHandle(
111 savedStateRegistryOwner,
112 viewModelStateRegistryOwner,
113 key,
114 defaultArgs
115 )
116 }
117
118 internal val ViewModelStoreOwner.savedStateHandlesVM: SavedStateHandlesVM
119 get() =
120 ViewModelProvider.create(
121 owner = this,
122 factory =
123 object : ViewModelProvider.Factory {
createnull124 override fun <T : ViewModel> create(
125 modelClass: KClass<T>,
126 extras: CreationExtras
127 ): T {
128 @Suppress("UNCHECKED_CAST") return SavedStateHandlesVM() as T
129 }
130 }
131 )[VIEWMODEL_KEY, SavedStateHandlesVM::class]
132
133 internal val SavedStateRegistryOwner.savedStateHandlesProvider: SavedStateHandlesProvider
134 get() =
135 savedStateRegistry.getSavedStateProvider(SAVED_STATE_KEY) as? SavedStateHandlesProvider
136 ?: throw IllegalStateException(
137 "enableSavedStateHandles() wasn't called " +
138 "prior to createSavedStateHandle() call"
139 )
140
141 internal class SavedStateHandlesVM : ViewModel() {
142 val handles = mutableMapOf<String, SavedStateHandle>()
143 }
144
145 /**
146 * This single SavedStateProvider is responsible for saving the state of every SavedStateHandle
147 * associated with the SavedState/ViewModel pair.
148 */
149 internal class SavedStateHandlesProvider(
150 private val savedStateRegistry: SavedStateRegistry,
151 viewModelStoreOwner: ViewModelStoreOwner
152 ) : SavedStateRegistry.SavedStateProvider {
153 private var restored = false
154 private var restoredState: SavedState? = null
155
<lambda>null156 private val viewModel by lazy { viewModelStoreOwner.savedStateHandlesVM }
157
saveStatenull158 override fun saveState(): SavedState {
159 return savedState {
160 // Ensure that even if ViewModels aren't recreated after process death and
161 // recreation
162 // that we keep their state until they are recreated
163 restoredState?.let { putAll(it) }
164 // But if we do have ViewModels, prefer their state over what we may
165 // have restored
166 viewModel.handles.forEach { (key, handle) ->
167 val savedState = handle.savedStateProvider().saveState()
168 if (savedState.read { !isEmpty() }) {
169 putSavedState(key, savedState)
170 }
171 }
172
173 // After we've saved the state, allow restoring a second time
174 restored = false
175 }
176 }
177
178 /** Restore the state from the SavedStateRegistry if it hasn't already been restored. */
performRestorenull179 fun performRestore() {
180 if (!restored) {
181 val newState = savedStateRegistry.consumeRestoredStateForKey(SAVED_STATE_KEY)
182 restoredState = savedState {
183 restoredState?.let { putAll(it) }
184 newState?.let { putAll(it) }
185 }
186 restored = true
187 // Grab a reference to the ViewModel for later usage when we saveState()
188 // This ensures that even if saveState() is called after the Lifecycle is
189 // DESTROYED, we can still save the state
190 viewModel
191 }
192 }
193
194 /** Restore the state associated with a particular SavedStateHandle, identified by its [key] */
consumeRestoredStateForKeynull195 fun consumeRestoredStateForKey(key: String): SavedState? {
196 performRestore()
197 val state = restoredState ?: return null
198 if (state.read { !contains(key) }) return null
199
200 val result = state.read { getSavedStateOrNull(key) ?: savedState() }
201 state.write { remove(key) }
202 if (state.read { isEmpty() }) {
203 this.restoredState = null
204 }
205
206 return result
207 }
208 }
209
210 // it reconnects existent SavedStateHandles to SavedStateRegistryOwner when it is recreated
211 internal class SavedStateHandleAttacher(private val provider: SavedStateHandlesProvider) :
212 LifecycleEventObserver {
213
onStateChangednull214 override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
215 check(event == Lifecycle.Event.ON_CREATE) { "Next event must be ON_CREATE, it was $event" }
216 source.lifecycle.removeObserver(this)
217 // onRecreated() is called after the Lifecycle reaches CREATED, so we
218 // eagerly restore the state as part of this call to ensure it consumed
219 // even if no ViewModels are actually created during this cycle of the Lifecycle
220 provider.performRestore()
221 }
222 }
223
224 /**
225 * A key for [SavedStateRegistryOwner] that corresponds to [ViewModelStoreOwner] of a [ViewModel]
226 * that is being created.
227 */
228 @JvmField
229 public val SAVED_STATE_REGISTRY_OWNER_KEY: CreationExtras.Key<SavedStateRegistryOwner> =
230 CreationExtras.Key<SavedStateRegistryOwner>()
231
232 /** A key for [ViewModelStoreOwner] that is an owner of a [ViewModel] that is being created. */
233 @JvmField
234 public val VIEW_MODEL_STORE_OWNER_KEY: CreationExtras.Key<ViewModelStoreOwner> =
235 CreationExtras.Key<ViewModelStoreOwner>()
236
237 /** A key for default arguments that should be passed to [SavedStateHandle] if needed. */
238 @JvmField
239 public val DEFAULT_ARGS_KEY: CreationExtras.Key<SavedState> = CreationExtras.Key<SavedState>()
240