本文译自「Why Your App Keeps Forgetting Everything」,原文链接medium.com/mobile-app-...,由Android Dev Nexus发布于2025年6月13日。

不知你有没有发现了一个让许多 Android 开发者困惑的关键问题:ViewModel 和 savedInstanceState 解决的是不同的问题,并且拥有不同的生命周期。
让我来解释一下你的测试中究竟发生了什么,以及为什么这两种机制都存在。
Android 中的两种"死亡"类型
Android 应用可以通过两种截然不同的方式"死亡":

1. 配置变更(屏幕旋转等)
- Activity/Fragment:死亡并重建
- ViewModel:幸存!🎉
- savedInstanceState:也幸存,但此时你并不需要它
2. 进程死亡(应用最小化、内存不足等)
- Activity/Fragment:死亡
- ViewModel:也死亡!💀
- savedInstanceState:幸存并成为你的生命线
测试结果不撒谎
Kotlin
// 进程死亡时会发生什么:
// 最小化应用程序之前:
class MyViewModel : ViewModel() {
var userData = "Important data"
var counter = 42
}
// 重新打开应用程序后:
// - 新的 ViewModel 实例已创建(旧数据已消失)
// - 但是 onCreate() 接收了已保存数据的 savedInstanceState 包
// - 你需要手动从 savedInstanceState 恢复 ViewModel
你猜测的完全正确:ViewModel 需要额外的步骤来存储/恢复数据,即使进程终止。
完整的解决方案:两者结合
以下是现代 Android 开发处理这个双层系统的方法:
Kotlin
class MainActivity : AppCompatActivity() {
private lateinit var viewModel: MyViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this)[MyViewModel::class.java]
// 检查我们是否正在从进程死亡中恢复
if (savedInstanceState != null && viewModel.isEmpty()) {
// 从 savedInstanceState 恢复 ViewModel
val userData = savedInstanceState.getString("user_data", "")
val counter = savedInstanceState.getInt("counter", 0)
viewModel.restoreFromSavedState(userData, counter)
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
// 保存关键的 ViewModel 数据以避免进程死亡
outState.putString("user_data", viewModel.userData)
outState.putInt("counter", viewModel.counter)
}
}
class MyViewModel : ViewModel() {
var userData: String = ""
var counter: Int = 0
fun isEmpty(): Boolean = userData.isEmpty() && counter == 0
fun restoreFromSavedState(userData: String, counter: Int) {
this.userData = userData
this.counter = counter
}
}
当每个机制生效时

让我来向你展示一下不同场景下的具体情况:
场景 1:屏幕旋转
Bash
Before rotation:
- ViewModel: Alive with data ✅
- savedInstanceState: Gets saved but not really needed
After rotation:
- ViewModel: Same instance, data intact ✅
- savedInstanceState: Available but redundant
- Result: ViewModel data is immediately available
场景2:应用程序最小化→重新打开(进程死亡)
Bash
Before minimizing:
- ViewModel: Alive with data ✅
- onSaveInstanceState(): Saves critical data to Bundle
After reopening:
- ViewModel: NEW instance, no data ❌
- savedInstanceState: Contains saved data ✅
- Result: Must restore ViewModel from savedInstanceState
场景 3:应用程序最小化 → 重新打开(进程存活)
Bash
Before minimizing:
- ViewModel: Alive with data ✅
After reopening:
- ViewModel: Same instance, data intact ✅
- savedInstanceState: null (no recreation happened)
- Result: ViewModel data is immediately available
那么,为什么存在这个双层系统?
ViewModel 处理常见情况:
- 复杂的 UI 状态,你不希望在频繁旋转屏幕时丢失。
- 任何需要耗费大量资源重新创建的内容。
savedInstanceState 处理特殊情况:
- 进程死亡难以预测。但当它发生时,用户希望其状态能够持久保存。
- 小而关键的数据片段(例如用户输入、滚动位置)。
- 简单的 UI 状态,对用户体验至关重要。
Bundle 大小的现实检验
以下是一些可以帮你免去调试麻烦的事情:Bundle 并非无限大的存储空间。你大约有 1MB 的空间可用,超过这个限制会导致 崩溃,让你质疑自己的人生选择。
保持 onSavedInstanceState 数据精简。保存用户写到一半的电子邮件草稿,而不是整个联系人列表。
现代方法:SavedStateHandle

Google 意识到这种手动操作非常繁琐,因此他们创建了 SavedStateHandle :
Kotlin
class MyViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {
// 这将自动保留配置更改和进程终止!
var userData: String
get() = savedStateHandle.get<String>("user_data") ?: ""
set(value) = savedStateHandle.set("user_data", value)
var counter: Int
get() = savedStateHandle.get<Int>("counter") ?: 0
set(value) = savedStateHandle.set("counter", value)
// 无需手动恢复!
}
对于以下情况,为什么仍然需要手动 onSaveInstanceState?
1. 不属于 ViewModel 的 UI 特定状态
Kotlin
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
// 这些不属于你的业务逻辑层
outState.putInt("scroll_position", recyclerView.computeVerticalScrollOffset())
outState.putBoolean("is_toolbar_expanded", collapsingToolbar.isExpanded)
outState.putParcelable("dialog_state", currentDialog?.onSaveInstanceState())
}
2. Fragment 参数和 Activity Extras
Kotlin
// 这些需要在进程终止后继续存在,但不是 ViewModel 状态
class DetailFragment : Fragment() {
companion object {
fun newInstance(itemId: String) = DetailFragment().apply {
arguments = Bundle().apply { // 这在内部使用 savedInstanceState
putString("item_id", itemId)
}
}
}
}
3. 视图状态过于特定于 UI
Kotlin
// 诸如 EditText 光标位置、焦点状态等。
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt("edit_text_selection_start", editText.selectionStart)
outState.putInt("edit_text_selection_end", editText.selectionEnd)
}
把手弄脏(动手试一试)
你可以强制终止进程进行测试:
bash
# Kill your app process
adb shell am kill com.yourpackage.name
# Or use "Don't keep activities" in Developer Options
这将帮助你验证状态恢复是否正确进行。
关键洞察
ViewModel 非常适合配置更改,但需要帮助应对进程死亡。
现代方法是在 ViewModel 中使用 SavedStateHandle,它可以自动处理这两种情况。如果你尚未使用它,则需要手动执行 savedInstanceState → ViewModel 的恢复过程。
这个双层系统看似复杂,但实际上非常优雅:常见情况(配置更改)的快速恢复,以及罕见情况(进程死亡)的可靠恢复。
祝你编码愉快,愿你的状态始终持久!🚀
欢迎搜索并关注 公众号「稀有猿诉」 获取更多的优质文章!
保护原创,请勿转载!