使用rememberSaveable+mutableStateOf的页面退后台时App崩溃
问题场景
在NavHost的目标页面中使用var selectedOption by rememberSaveable { mutableStateOf("") }这类代码时,App切换至后台会触发崩溃,当前使用的依赖版本为androidx.navigation:navigation-compose:2.5.2,此前相关问题的旧修复方案及官方Issue建议均无法解决。
崩溃日志
java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = [Landroidx.compose.runtime.MutableState;) at android.os.Parcel.writeSerializable(Parcel.java:1833) at android.os.Parcel.writeValue(Parcel.java:1780) at android.os.Parcel.writeList(Parcel.java:1045) at android.os.Parcel.writeValue(Parcel.java:1729) at android.os.Parcel.writeMapInternal(Parcel.java:896) at android.os.Parcel.writeMap(Parcel.java:878) at android.os.Parcel.writeValue(Parcel.java:1694) at android.os.Parcel.writeMapInternal(Parcel.java:896) at android.os.Parcel.writeMap(Parcel.java:878) at android.os.Parcel.writeValue(Parcel.java:1694) at android.os.Parcel.writeList(Parcel.java:1045) at android.os.Parcel.writeValue(Parcel.java:1729) at android.os.Parcel.writeArrayMapInternal(Parcel.java:928) at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1584) at android.os.Bundle.writeToParcel(Bundle.java:1253) at android.os.Parcel.writeBundle(Parcel.java:997) at android.os.Parcel.writeValue(Parcel.java:1698) at android.os.Parcel.writeArrayMapInternal(Parcel.java:928) ... at android.app.IActivityTaskManager$Stub$Proxy.activityStopped(IActivityTaskManager.java:4505) at android.app.servertransaction.PendingTransactionActions$StopInfo.run(PendingTransactionActions.java:145) at android.os.Handler.handleCallback(Handler.java:883) at android.os.Handler.dispatchMessage(Handler.java:100) at android.os.Looper.loop(Looper.java:214) at android.app.ActivityThread.main(ActivityThread.java:7356) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930) Caused by: java.io.NotSerializableException: androidx.compose.runtime.ParcelableSnapshotMutableState at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1240) at java.io.ObjectOutputStream.writeArray(ObjectOutputStream.java:1434) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1230) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:354) at android.os.Parcel.writeSerializable(Parcel.java:1828) at android.os.Parcel.writeValue(Parcel.java:1780) at android.os.Parcel.writeList(Parcel.java:1045) at android.os.Parcel.writeValue(Parcel.java:1729) at android.os.Parcel.writeMapInternal(Parcel.java:896) at android.os.Parcel.writeMap(Parcel.java:878) ...
崩溃原因
从日志核心异常java.io.NotSerializableException: androidx.compose.runtime.ParcelableSnapshotMutableState可以看出:系统在App退至后台时尝试序列化保存页面状态,但ParcelableSnapshotMutableState仅实现了Parcelable接口,并未实现Serializable接口,导致序列化失败。在NavHost场景下,导航组件的状态保存机制与Compose的rememberSaveable存在兼容性冲突,错误地触发了Serializable序列化流程而非Parcelable。
解决方案
方案1:为NavHost外层添加SaveableStateHolder
手动为NavHost提供自定义的SaveableStateHolder,确保页面状态通过Compose的机制正确持久化,避免导航组件的默认逻辑干扰:
val navController = rememberNavController() SaveableStateHolder { NavHost(navController = navController, startDestination = "home") { composable("home") { // 首页内容 } composable("detail") { var selectedOption by rememberSaveable { mutableStateOf("") } // 详情页逻辑 } } }
方案2:自定义rememberSaveable的Saver
通过saver参数指定状态的序列化逻辑,只序列化状态的值而非State对象本身:
var selectedOption by rememberSaveable( saver = mutableStateSaver( save = { it.value }, restore = { mutableStateOf(it) } ) ) { mutableStateOf("") }
该方式直接操作状态的原始值(此处为字符串,天然支持序列化),绕开了State对象的序列化问题。
方案3:升级navigation-compose依赖版本
尝试将androidx.navigation:navigation-compose升级至2.6.0及以上版本,新版本大概率修复了导航组件与rememberSaveable的兼容性问题,彻底解决此类崩溃。
内容的提问来源于stack exchange,提问作者TheYogi

