在 Android 架构组件中,ViewModel 是最容易“用对,但理解错”的组件之一。
很多开发者知道它可以“扛住旋转屏幕”,却始终说不清:
- ViewModel 到底活多久?
- 它和 Activity / Fragment / Application 是什么关系?
- 为什么官方总说 ViewModel 不要持有 Context?
本文将从 生命周期视角,一次性把这些问题讲清楚。
一、先给结论(直接记住)
ViewModel 的生命周期:
- 比 Activity / Fragment 的 View 生命周期 长
- 但 一定短于 Application
- 它不依附于 Context,而是依附于 ViewModelStoreOwner
这三点,是理解 ViewModel 的根基。
二、ViewModel 并不依附于 Context
很多误解都来自一句错误认知:
“ViewModel 是和 Activity 绑定的”
更准确的说法应该是:
ViewModel 绑定的是 ViewModelStoreOwner
常见的 ViewModelStoreOwner 有:
ComponentActivity(Activity 级 ViewModel)Fragment(Fragment 级 ViewModel)NavBackStackEntry(Navigation 作用域)
正因为如此:
Activity 因配置变化被销毁重建时,ViewModel 才能继续存活。
三、ViewModel 与 Activity 生命周期对照
1️⃣ 屏幕旋转(配置变化)
- 旧 Activity →
onDestroy - 新 Activity →
onCreate - ViewModel 不会销毁
- 新 Activity 复用原 ViewModel
这正是 ViewModel 被设计出来的核心价值。
2️⃣ Activity 真正结束(finish)
- Activity 被用户返回或
finish() ViewModelStore被清空- 调用
ViewModel.onCleared() - ViewModel 生命周期结束
⚠️ 注意:
- ViewModel 没有
onStart / onResume / onPause - 它只关心一件事:是否还被 Owner 需要
ViewModel 生命周期:相较于 Activity / Application
1) ViewModel 的“归属”与生命周期边界
ViewModel 的生命周期不是跟着 Activity 的 onCreate/onDestroy 逐回调走的。它本质上是跟随 ViewModelStoreOwner(通常是 Activity 或 Fragment,或 NavBackStackEntry)的 ViewModelStore 存活。
-
Activity 级 ViewModel:以 Activity 作为
ViewModelStoreOwner创建(如by viewModels()/ViewModelProvider(this))- 生命周期边界:从首次获取创建开始,一直存活到该 Activity 的
ViewModelStore被清空(通常是 Activity 真正结束时)。 - 配置变更(旋转屏幕、语言切换、夜间模式等):Activity 会重建,但同一个 ViewModel 会被复用(不会因旋转而重建)。
- 生命周期边界:从首次获取创建开始,一直存活到该 Activity 的
-
Fragment 级 ViewModel:以 Fragment 作为 owner 创建(
by viewModels()/ViewModelProvider(this))- 生命周期边界:直到该 Fragment 的
ViewModelStore清空(通常是 Fragment 被真正移除时)。 - 注意:Fragment 的 ViewModel 与 Fragment 的 View 生命周期(
onCreateView/onDestroyView)不同步:View 销毁不等于 ViewModel 销毁。
- 生命周期边界:直到该 Fragment 的
-
NavGraph / BackStackEntry 级 ViewModel(常见于 Navigation 组件):owner 是某个
NavBackStackEntry- 生命周期边界:直到该 back stack entry 出栈。
2) 相较 Activity:核心差异
Activity
- 典型生命周期:
onCreate → onStart → onResume → … → onPause → onStop → onDestroy - Activity 可能因为:
- 配置变更(旋转等)而
onDestroy后立即重建 - 系统回收、进程死亡而消失
- 配置变更(旋转等)而
- Activity 销毁时:
- 若是配置变更导致的重建:旧 Activity
onDestroy,但ViewModelStore通常会被保留/转移,新 Activity 继续拿到同一个 ViewModel - 若是“真正结束”(
finish()、返回退出并移除等):ViewModelStore被清空,ViewModel 触发onCleared()
- 若是配置变更导致的重建:旧 Activity
ViewModel
- 不直接感知
onResume/onPause这类前后台回调(不以可见性为边界) - 关注点:
- 跨配置变更存活
- 在 owner 真正结束时被清理(触发
onCleared())
- 设计目的:承载 UI 状态与业务数据,避免旋转导致数据丢失与重复请求。
3) 相较 Application:生命周期范围与职责
Application
- 进程级别:通常跟随应用进程生命周期
- 创建:进程启动时
Application#onCreate - 结束:进程被系统杀死或用户强制结束
- 特性:
- 全局单例、生命周期最长
- 易被滥用为“全局状态桶”(增加耦合、难测试、易泄漏)
- 不适合承载页面级 UI 状态
ViewModel
- 页面/导航图级别:明显短于 Application
- 不应持有
Activity/Fragment/View引用(否则内存泄漏) - 若需要 Context:
- 使用
AndroidViewModel(持有Applicationcontext) - 或更推荐:依赖注入传入
ApplicationContext
- 使用
4) 什么时候会销毁 ViewModel?
触发 ViewModel onCleared() 的典型场景:
- owner(Activity / Fragment / NavBackStackEntry)真正结束,其
ViewModelStore被清空 - 对 Activity 而言:
finish()且不再重建- 返回键退出并从返回栈移除
- 对 Fragment 而言:
- Fragment 被从
FragmentManager中移除(注意不是仅onDestroyView)
- Fragment 被从
- 对 Navigation 而言:
- 对应的
NavBackStackEntry出栈
- 对应的
通常不会触发 onCleared() 的场景:
- 旋转屏幕等配置变更(ViewModel 继续存活并复用)
- 暂时进入后台(Activity
onStop但仍在返回栈中) - Fragment 的
onDestroyView(仅视图销毁,VM 通常仍存活)
5) 对照表:一眼看清差异
| 维度 | Activity | ViewModel(以 Activity 为 owner) | Application |
|---|---|---|---|
| 作用范围 | 单个页面容器 | 页面级状态/业务数据管理 | 全局/进程级 |
| 配置变更 | 通常重建 | 通常保留复用 | 不受影响 |
| 进入后台 | onStop | 仍存活(只要 owner 还在栈中) | 存活 |
| 真正退出页面 | onDestroy | onCleared() | 仍存活 |
| 进程被杀 | 销毁 | 销毁(状态需外部恢复) | 销毁 |
| 适合存放 | UI 控制、导航、生命周期回调 | UI 状态、加载/缓存、业务逻辑 | 全局初始化、全局服务入口 |
6) 实践建议(避免常见坑)
- 不要在 ViewModel 里持有 Activity/Fragment/View 引用(避免内存泄漏)
- 需要 Context 时优先用
ApplicationContext(DI 传入),或用AndroidViewModel - 需要跨进程死亡恢复数据:
- 用
SavedStateHandle(适合小状态) - 或持久化:Room / DataStore / SharedPreferences 等
- 结论:ViewModel 只保证 跨配置变更,不保证 进程死亡后恢复
- 用