Status Update
Comments
il...@google.com <il...@google.com>
il...@google.com <il...@google.com> #2
With the existing support for
fun <T : Any> SavedStateHandle.saveableMutableStateOf(
key: String,
saver: Saver<T, out Any> = autoSaver(),
init: () -> T
): MutableState<T> {
// To be honest, I'm not sure why rememberSaveable does this
// vs just have the saver of type Saver<T, Any>
@Suppress("UNCHECKED_CAST")
(saver as Saver<T, Any>)
// value is restored using the SavedStateHandle or created via [init] lambda
val value = mutableStateOf(run {
val restored = get<Bundle>(key)?.get("value")?.let {
saver.restore(it)
}
restored ?: init()
})
// Hook up saving the MutableState to the SavedStateHandle
setSavedStateProvider(key) {
val saverScope = SaverScope { true }
with (saver) {
bundleOf("value" to saverScope.save(value.value))
}
}
return value
}
Which then can be used like:
class SavingTestViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
var saved by savedStateHandle.saveableMutableStateOf("key") { "initialState" }
}
This seems like a reasonable utility to provide as part of Lifecycle itself, so we'll leave this issue open to track adding to to Lifecycle directly.
va...@google.com <va...@google.com> #3
For the version added to Lifecycle, I wonder if matching the signature of rememberSaveable
would be good for consistency:
var saved by savedStateHandle.saveable("key") { mutableStateOf("initialState") }
That would also allow saving a state holder which may not want to be wrapped in a MutableState
which exposes a custom Saver
(like rememberSaveable
allows):
val myStateHolder = savedStateHandle.saveable("key", saver = MyStateHolder.Saver) { MyStateHolder() }
That could look something like
fun <T : Any> SavedStateHandle.saveable(
key: String,
saver: Saver<T, out Any> = autoSaver(),
init: () -> T,
): T {
@Suppress("UNCHECKED_CAST")
saver as Saver<T, Any>
// value is restored using the SavedStateHandle or created via [init] lambda
val value = get<Bundle?>(key)?.get("value")?.let(saver::restore) ?: init()
// Hook up saving the state to the SavedStateHandle
setSavedStateProvider(key) {
bundleOf("value" to with(saver) { SaverScope { true }.save(value) })
}
return value
}
il...@google.com <il...@google.com>
al...@gmail.com <al...@gmail.com> #4
Are these states supposed to survive user completion of the activity? I.e. are these saved states recoverable when killing the app and starting it again?
Compared to SavedInstanceState (which do not according to:
va...@google.com <va...@google.com> #5
No, the states would follow the same lifetime as other state stored in SavedStateHandle
, which follows the same rules as Saved instance state
from that table (serialized to disk, survives process death, only supports simple, small objects).
The goal isn't to all state to survive user completion of the activity, but to allow using the same Saver
abstraction for rememberSaveable
and SavedStateHandle
.
ap...@google.com <ap...@google.com> #6
Branch: androidx-main
commit 0d614efac51fd8308d3a8d1a4ac3a86fd89caa03
Author: Jeremy Woods <jbwoods@google.com>
Date: Tue Mar 01 17:01:49 2022
Add experimental SavedStateHandle Saver support
Added an experimental API at the lifecycle level to provide a temporary
hook for SavedStateHandle into compose Saver while we work on a longer
term integration plan.
RelNote: "The `lifecycle-viewmodel-compose` module now provides
`SavedStateHandleSaver`, an experimental API that ensures values in a
`SavedStateHandle` are integrated correctly with the same saved instance
state that `rememberSaveable` uses."
Test: N/A
Bug: 195689777
Change-Id: Ia88b79c56a5f82263ddaf436777dfc1c4ab73704
M lifecycle/lifecycle-viewmodel-compose/api/restricted_current.txt
M lifecycle/lifecycle-viewmodel-savedstate/src/main/java/androidx/lifecycle/SavedStateHandle.kt
M lifecycle/lifecycle-viewmodel-compose/api/current.txt
M lifecycle/lifecycle-viewmodel-compose/api/public_plus_experimental_current.txt
M lifecycle/lifecycle-viewmodel-compose/build.gradle
A lifecycle/lifecycle-viewmodel-compose/src/main/java/androidx/lifecycle/viewmodel/compose/SavedStateHandleSaveableApi.kt
A lifecycle/lifecycle-viewmodel-compose/src/androidTest/java/androidx/lifecycle/viewmodel/compose/SavedStateHandleSaverTest.kt
A lifecycle/lifecycle-viewmodel-compose/src/main/java/androidx/lifecycle/viewmodel/compose/SavedStateHandleSaver.kt
jb...@google.com <jb...@google.com> #7
This has been added internally and will be in the Lifecycle 2.5.0-alpha05
release.
Description
Compose provides
rememberSaveable
, along with accompanying tools likeSaver
andSaveableStateRegistry
as its mechanisms for storing saved instance state. Currently, there isn't a provided interoperability layer between these tools andSavedStateHandle
.If
SavedStateHandle
isn't used in theViewModel
, state that is hoisted from@Composable
s toViewModel
s won't be stored to saved instance state. This might either cause bugs due to state loss upon process death, or act as a barrier to hoisting state sufficiently if it seems likerememberSaveable
has to be used to remember state across process recreation.The proposal is to provide the necessary hooks for
SavedStateHandle
withSaver
so that any state holder that is being saved viarememberSaveable
could also be saved withSavedStateHandle
.