Compose Saveable自定义Saver恢复时出现“本应不可能”的IndexOutOfBoundsException异常求助
Compose Saveable自定义Saver恢复时出现“本应不可能”的IndexOutOfBoundsException异常求助
兄弟我太懂这种“逻辑闭环到自己都拍胸脯没问题,但就是崩给你看”的崩溃有多闹心了!我来帮你捋捋可能的原因和解决办法:
最可能的元凶:遗留的旧持久化数据
你现在的代码逻辑看起来确实没问题——save要么存空字符串,要么存type|uri格式,restore也分情况处理了。但别忘了rememberSaveable的数据是持久化存储的(默认存在App的SharedPreferences里),如果你的App之前有过版本迭代,或者你中途修改过Saver的save逻辑,那用户设备里可能还留着旧的不符合当前格式的数据!
举个例子:假设你之前的Saver版本里,save方法只返回了data.type(没有拼接|${data.uri}),那旧数据就是个纯type字符串,没有分隔符。当用户升级到新代码后,restore时split这个字符串就只会得到一个元素,直接触发IndexOutOfBoundsException。
其他可能的小概率场景
- 极端情况下,Uri的
toString()方法返回了空字符串?但就算是这样,save的结果是type|,split后还是会得到两个元素(第二个是空字符串),不会触发越界,最多是toUri()时可能出问题。 - 并发或异常中断导致保存了不完整的字符串:比如save方法执行到一半App崩溃,导致SharedPreferences里存了半截字符串(比如只有type没有|uri),这种情况概率极低,但也不是完全不可能。
解决方案:给restore加一层防御性判断
不管原因是什么,最稳妥的办法是在restore时增加对分割后元素数量的校验,避免直接取索引1:
override fun restore(value: String): MutableState<SelectedUriData?> { if (value.isEmpty()) { return mutableStateOf(null) } else { val parts = value.split("\\|", limit = 2) // 新增:判断分割后的元素数量,不足2就返回null if (parts.size < 2) { return mutableStateOf(null) } val type = parts[0] val uri = parts[1].toUri() return mutableStateOf(SelectedUriData(uri, type)) } }
进阶优化:给保存的数据加版本标识
如果以后还要修改Saver的格式,建议给保存的字符串加个版本前缀,比如:
override fun SaverScope.save(value: MutableState<SelectedUriData?>): String { val data = value.value return if (data == null) { "" } else { // 加版本前缀,方便后续兼容旧数据 "v1|${data.type}|${data.uri}" } }
对应的restore逻辑就可以先判断版本,不同版本用不同的解析方式,彻底避免旧数据兼容问题。
内容来源于stack exchange
相关产品推荐
相关产品推荐

