3. onRestoreInstanceState与onCreate中恢复的区别
当 Activity 因配置变更(如屏幕旋转)或系统资源回收而重建时,开发者需要在 onCreate 或 onRestoreInstanceState 中从保存的 Bundle 恢复界面状态。两者都能拿到同一个 Bundle 对象,但调用时机、参数行为和使用场景存在关键差异。错误选择会导致数据重复恢复、空指针或状态不一致。
调用时机差异
| 对比维度 | onCreate(Bundle) | onRestoreInstanceState(Bundle) |
|---|---|---|
| 调用顺序 | 在 onStart 之前,Activity 生命周期的第一个回调 | 在 onStart 之后,onResume 之前 |
| Bundle 有效性 | 首次启动时 bundle == null;重建时 bundle != null | 仅在 Activity 确实是从已保存状态重建时才被调用,此时 bundle 永远不为 null |
| 超类调用 | 通常 super.onCreate(savedInstanceState) 放在开头 | 必须先调用 super.onRestoreInstanceState(savedInstanceState),否则系统无法恢复 View 树状态 |
| View 树状态 | 此时视图尚未完成布局,View 实例刚被创建 | View 树已建立,系统自动恢复子 View 状态(如 EditText 文本),开发者只需恢复自定义数据 |
关键规律:onCreate 中 bundle 可能为 null,必须做空判断;onRestoreInstanceState 中 bundle 必然非空,但该方法只在重建时调用一次,不会在首次创建时触发。
空值判断 —— 避免 NullPointerException
正确区分两种恢复方式的空值处理是安全编码的基础。
class MyActivity : AppCompatActivity() {
private var restoredText: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 方式一:在 onCreate 中恢复(必须判空)
if (savedInstanceState != null) {
restoredText = savedInstanceState.getString("my_key")
}
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
// 方式二:在 onRestoreInstanceState 中恢复(无需判空)
restoredText = savedInstanceState.getString("my_key")
}
override fun onSaveInstanceState(outState: Bundle) {
outState.putString("my_key", restoredText)
super.onSaveInstanceState(outState)
}
}
常见错误:在 onRestoreInstanceState 中直接使用
savedInstanceState?.getString(...) 虽然语法合法,但会掩盖一个事实:该方法只在重建时调用,bundle 一定非 null。强行添加 ? 会给代码阅读者传递错误信号。
避免重复恢复
许多开发者同时在 onCreate 和 onRestoreInstanceState 中恢复同一份数据,导致状态被覆盖两次。例如用户输入的文字先被 onCreate 恢复,接着被 super.onRestoreInstanceState 自动恢复的 View 状态覆盖,最终丢失正确内容。
推荐做法:将自定义数据的恢复统一放在 onRestoreInstanceState 中,而在 onCreate 中只恢复不可重复的轻量标记。或者使用标志位控制只恢复一次:
class MyActivity : AppCompatActivity() {
private var restoredFromCreate = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (savedInstanceState != null) {
// 仅恢复必须提前处理的逻辑,例如列表位置
restoredFromCreate = true
// 避免在这里恢复 EditText 等 View 直接管理的数据
}
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
// 如果已在 onCreate 恢复过,跳过重复恢复
if (!restoredFromCreate) {
// 恢复自定义数据
}
}
}
最佳实践:对于 EditText、CheckBox 等内置 View 状态,永远不要手动恢复 —— 交给
super.onRestoreInstanceState 自动处理。开发者只需关注与 View 无关的成员变量(如网络请求 ID、滚动位置记录等)。
常见错误与调试
- 在 onCreate 中忘记判空:首次运行时 savedInstanceState 为 null,直接调用
getString()会导致 NoSuchMethodException(针对 Bundle 对象调用方法?实际上是 NullPointerException)。 - 在 onRestoreInstanceState 中重复调用父类:忘记调用
super.onRestoreInstanceState(savedInstanceState)会导致 View 树状态丢失,EditText 的文本、CheckBox 的选中状态无法自动恢复。 - 同时使用两种方式恢复同一数据:例如在 onCreate 中设置了 title,又在 onRestoreInstanceState 中又设置一次,若顺序不当会导致最终状态错误。推荐统一使用 onRestoreInstanceState。
- 误以为 onRestoreInstanceState 一定会被调用:当 Activity 不是由于“保存状态后重建”时(如系统杀进程后用户重新打开),该方法不会被调用,此时应从持久化存储恢复。
调试技巧:在 onSaveInstanceState 和 onRestoreInstanceState 中插入日志,配合 Logcat 观察 Bundle 内容与调用次数。使用
adb shell dumpsys activity 查看 Activity 记录中的 savedState 数据。
性能与稳定性优化
将大数据对象(如图片位图、复杂列表)的保存与恢复移出主线程,或在 onRestoreInstanceState 中避免冗长操作(因为该回调仍在主线程)。对于非必要数据,考虑使用 ViewModel 替代 Bundle 恢复,减少序列化开销。