异常堆栈:
E FATAL EXCEPTION: main
Process: com.lie.composeanimationkit, PID: 8609
java.lang.IllegalArgumentException: Failed requirement.
at androidx.compose.runtime.saveable.ListSaverKt$listSaver$1.invoke(ListSaver.kt:39)
at androidx.compose.runtime.saveable.ListSaverKt$listSaver$1.invoke(ListSaver.kt:33)
at androidx.compose.runtime.saveable.SaverKt$Saver$1.save(Saver.kt:66)
at androidx.compose.runtime.saveable.RememberSaveableKt$rememberSaveable$1$valueProvider$1.invoke(RememberSaveable.kt:100)
at androidx.compose.runtime.saveable.RememberSaveableKt$rememberSaveable$1.invoke(RememberSaveable.kt:102)
at androidx.compose.runtime.saveable.RememberSaveableKt$rememberSaveable$1.invoke(RememberSaveable.kt:98)
at androidx.compose.runtime.DisposableEffectImpl.onRemembered(Effects.kt:81)
at androidx.compose.runtime.CompositionImpl$RememberEventDispatcher.dispatchRememberObservers(Composition.kt:802)
at ...
定位到代码处:
@Composable
fun rememberLoadingButtonState(
initialState: LoadingState = LoadingState.Ready,
): LoadingButtonState = rememberSaveable(saver = LoadingButtonState.Saver) {
LoadingButtonState(initialState)
}
这里我定义了一个rememberLoadingButtonState方法,调用rememberSaveable来记录按钮的状态,
LoadingState是我定义的一个密封类,对应各种状态
sealed class LoadingState {
object Ready : LoadingState()
object Loading : LoadingState()
object Success : LoadingState()
object Error : LoadingState()
}
LoadingButtonState是状态的持有者,同时内部实现了Saver 来存储和读取状态
class LoadingButtonState(val currentState: LoadingState = LoadingState.Ready) {
companion object {
/**
* The default [Saver] implementation for [PagerState].
*/
val Saver: Saver<LoadingButtonState, *> = listSaver(
save = {
listOf<Any>(
it.currentState,
)
},
restore = {
LoadingButtonState(
currentState = it[0] as LoadingState,
)
}
)
}
}
问题原因:
定位到Saver类,看Saver的说明信息:
The Saver describes how the object of Original class can be simplified and converted into something which is Saveable.
What types can be saved is defined by SaveableStateRegistry, by default everything which can be stored in the Bundle class can be saved. The implementations can check that the provided value can be saved via SaverScope.canBeSaved
You can pass the implementations of this class as a parameter for rememberSaveable.
因为rememberSaveable实际是基于onSaveInstanceState实现的,具体可以看fundroid大佬的这篇文章:
Compose状态保存rememberSaveable原理解析
因此同样,Saver存储和读取的数据必须是能写进Bundle的基本数据类型或者Parcle,具体可以用SaverScope.canBeSaved()方法判断是否支持存储
了解问题原因之后就好办了,上面的代码中,Saver存储的是自定义的密封类对象,所以不支持Saver.save方法,那我们就改一下:
sealed class LoadingState(val code: Int) {
object Ready : LoadingState(0)
object Loading : LoadingState(1)
object Success : LoadingState(2)
object Error : LoadingState(3)
companion object {
fun getState(code: Int): LoadingState {
return when (code) {
0 -> Ready
1 -> Loading
2 -> Success
else -> Error
}
}
}
}
为了避免太大的改动,继续保留密封类的正常使用,可以参考这么去改LoadingState密封类:
思路其实类似与emun枚举类的getValue()方法
然后Saver处也要调整一下存储和写入的规则:
class LoadingButtonState(val currentState: LoadingState = LoadingState.Ready) {
companion object {
/**
* The default [Saver] implementation for [PagerState].
*/
val Saver: Saver<LoadingButtonState, *> = listSaver(
save = {
listOf<Any>(
it.currentState.code, //改为取基本数据类型code,存储
)
},
restore = {
LoadingButtonState(
currentState = LoadingState.getState(it[0] as Int), //根据code获取对应的密封类对象
)
}
)
}
}
然后其他地方都不需要调整,密封类对象和Saver的使用方式还是和之前的一样