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、滚动位置记录等)。
Activity 重建时 onCreate 与 onRestoreInstanceState 的调用时序 系统触发 Activity 重建 onCreate(Bundle) Bundle 可能为 null 需做空判断 onStart() onRestoreInstanceState(Bundle) Bundle 永远非 null 无需判空,但需先 super onResume()

常见错误与调试

  • 在 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 恢复,减少序列化开销。