一、ViewModel 的核心能力:跨配置重建
Android 的 Activity/Fragment 在屏幕旋转时会经历完整的销毁和重建流程,这会导致普通成员变量中的数据丢失。传统的解决方案是用 onSaveInstanceState() 将数据序列化保存再恢复,但这对大体积对象(如网络数据列表、Bitmap)效率极低且无法保存非序列化对象。ViewModel 的出现彻底解决了这个问题——ViewModel 存储于 ViewModelStore 中,而 ViewModelStore 的生命周期与 Activity 的配置变更无关。
但 ViewModel 的能力远不止这些。它还是 MVVM 架构的核心组件,承担着以下职责:
- 数据持有:持有 UI 所需的数据,生命周期长于 Activity/Fragment。
- 业务逻辑承载:承担数据获取、转换、缓存等业务逻辑,将 Activity/Fragment 解放为纯粹的 UI 控制器。
- Fragment 间通信:通过 Activity-scoped ViewModel 实现 Fragment 之间的数据共享。
- 协程集成:通过
viewModelScope自动管理协程生命周期。
二、源码解析:ViewModel 是如何”存活”下来的
2.1 核心组件关系图
┌──────────────────────────┐ |
2.2 NonConfigurationInstances 机制
核心在于 ViewModelStoreOwner 和 ViewModelStore。以 Activity 为例(AOSP 路径:/lifecycle/viewmodel/src/main/java/androidx/lifecycle/ViewModelProvider.kt),ComponentActivity 实现了 ViewModelStoreOwner 接口。Activity 首次创建时会 new ViewModelStore(),在配置变更重建时,系统通过 NonConfigurationInstances 机制将旧的 ViewModelStore 传递给新的 Activity 实例。
关键代码路径:
// ComponentActivity.java (androidx.activity) |
当 Activity 因屏幕旋转而销毁时,系统在 retainNonConfigurationInstances() 中将 ViewModelStore 保存;新建 Activity 时通过 getLastNonConfigurationInstance() 恢复。ViewModelStore 内部是一个 HashMap<String, ViewModel>,key 来自 ViewModel 类名,确保同名 ViewModel 获取的是同一个实例。
2.3 ComponentActivity 中的完整实现
// ComponentActivity.java (androidx.activity) |
2.4 ViewModelStore 的实现
// ViewModelStore.java |
2.5 Activity 销毁时 ViewModelStore 的清理时机
ViewModelStore 什么时候被清理?关键区别在于配置变更和真正的销毁:
// ComponentActivity.java |
这就是 ViewModel 能跨配置重建存活的核心逻辑——配置变更时 ViewModelStore 不执行 clear(),而是被 onRetainNonConfigurationInstance() 保留并传递给新 Activity。
2.6 Fragment 中的 ViewModel 存储
Fragment 的 ViewModelStore 存储在 FragmentManager 中:
// Fragment.java |
这个设计非常精妙——FragmentManager 的所有 Fragment 的 ViewModelStore 都被存储在一个特殊的 FragmentManagerViewModel 中,而这个 ViewModel 又存储在 Activity 的 ViewModelStore 中。因此:
- Activity 配置变更:Activity 的 ViewModelStore 被保留 -> FragmentManagerViewModel 被保留 -> 所有 Fragment 的 ViewModelStore 被保留
- Activity 真正销毁:Activity 的 ViewModelStore.clear() -> FragmentManagerViewModel.onCleared() -> 所有 Fragment 的 ViewModelStore.clear()
三、ViewModelProvider 与 Factory
ViewModelProvider 是获取 ViewModel 的门面类,通过 ViewModelStoreOwner 拿到 ViewModelStore。默认的 Factory 通过反射调用 ViewModel 的无参构造。如果 ViewModel 需要构造参数(如注入 Repository),需要使用自定义 Factory。
3.1 ViewModelProvider 的核心流程
// ViewModelProvider.kt |
3.2 自定义 Factory
当 ViewModel 需要构造参数(如注入 Repository)时,需要使用自定义 Factory:
class MyViewModel(private val repo: Repository) : ViewModel() |
3.3 AndroidViewModel 与 Factory
AndroidViewModel 是 ViewModel 的子类,在构造函数中接收 Application 实例:
// 使用 AndroidViewModel 的 Application Context |
3.4 SavedStateHandle 的 Factory
从 lifecycle-viewmodel-savedstate 开始,Google 推荐使用 SavedStateHandle 来保存进程重建后的状态:
class MySavedStateViewModel( |
SavedStateHandle 的底层原理:
- 它本质是一个
Bundle的包装器。 - 在 Activity 的
onSaveInstanceState()被调用时,SavedStateHandle中的数据被序列化到 Bundle 中。 - 进程重建后,数据从 Bundle 恢复。
- 这与 ViewModel 的
NonConfigurationInstances机制互补——ViewModelStore 负责跨配置变更存活,SavedStateHandle 负责跨进程死亡恢复。
四、ViewModelScope:协程的最佳拍档
ViewModel 实现的另一个重要扩展是 viewModelScope(来自 lifecycle-viewmodel-ktx)。它是一个绑定到 ViewModel 的 CoroutineScope,在 ViewModel 的 onCleared() 方法被调用时自动取消。这意味着启动在 viewModelScope 中的所有协程无需手动管理生命周期,彻底解决了”协程泄露导致内存泄漏”的问题。
4.1 viewModelScope 的实现
// lifecycle-viewmodel-ktx |
当 ViewModel 的 clear() 被调用时:
// ViewModel.java |
4.2 使用示例
class UserViewModel : ViewModel() { |
4.3 SupervisorJob vs Job
viewModelScope 使用 SupervisorJob() 作为协程的父 Job。这意味着:
- SupervisorJob:子协程的失败不会影响其他兄弟协程。如果一个
launch中的协程抛出异常被捕获,其他协程继续运行。 - 普通 Job:一个子协程失败,所有兄弟协程也会被取消。
这对 UI 场景非常重要——你不想因为一个数据加载失败而导致所有并行的请求都被取消。
五、Fragment 间共享 ViewModel
Fragment 之间共享 ViewModel 是 MVVM 架构中常见的需求。Android 推荐使用 Activity-scoped ViewModel 而非 Fragment 间接口回调:
// 在 Fragment 中使用 Activity-scoped ViewModel |
5.1 共享 ViewModel 的实现原理
activityViewModels() 委托的实现:
// FragmentViewModelLazy.kt |
关键在于 requireActivity().viewModelStore——使用的是 Activity 级别的 ViewModelStore 而非 Fragment 自己的。因此所有 Fragment 拿到的都是同一个 ViewModel 实例。
5.2 通过 Navigation Graph 作用域共享
Android Navigation 组件也支持 ViewModel 共享:
// 在 Navigation 中,ViewModel 可以绑定到 NavBackStackEntry |
navGraphViewModels 会在 NavGraph 的范围内保持 ViewModel,当该 NavGraph 被弹出栈时,ViewModel 的 onCleared() 才会被调用。
六、ViewModel 的生命周期与常见陷阱
6.1 ViewModel 的完整生命周期
Activity/Fragment 首次创建 |
6.2 常见陷阱
陷阱一:在 ViewModel 中持有 Activity/View/Context 引用
class BadViewModel : ViewModel() { |
陷阱二:过度使用 ViewModel
ViewModel 不是万能的。以下情况不应该使用 ViewModel:
- 简单的 Fragment 配置状态(用
onSaveInstanceState更合适) - 跨 Activity 共享数据(应该用 Repository 单例 + 持久化)
- 跨进程数据传递(应该用 ContentProvider 或文件)
- 一次性事件(如导航指令、Snackbar——这些应该用
SingleLiveEvent或Channel)
陷阱三:ViewModel 中发射数据到 UI 的时机
class MyViewModel : ViewModel() { |
七、ViewModel 与 SavedStateHandle 的协作
7.1 双层保护机制
配置变更(旋转屏幕) |
这是一个设计精巧的”双层保险”:
- 配置变更 → ViewModel 在内存中保持,零开销
- 进程死亡 → SavedStateHandle 将关键状态序列化,恢复后自动填充
7.2 实现示例
class SearchViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() { |
面试常考问题
Q1: ViewModel 与 onSaveInstanceState 如何选择?
A: 这是 Android 面试中的经典问题,考察候选人对组件边界的理解。
ViewModel(通过 NonConfigurationInstances):
- 存活场景:仅配置变更(屏幕旋转、语言切换、键盘可用性变化等)
- 存储位置:内存(ViewModelStore 由 Activity 实例持有,通过 ActivityClientRecord 传递)
- 容量:无限制(受设备 RAM 限制)
- 数据类型:任何 Java/Kotlin 对象
- 适用场景:UI 数据列表、网络请求结果、Bitmap、Repository 实例
- 性能:零开销(纯内存操作,无序列化/反序列化)
onSaveInstanceState(通过 Bundle):
- 存活场景:配置变更 + 进程死亡(系统杀进程 + 用户返回)
- 存储位置:系统进程内存 + 磁盘(ActivityManagerService 持有)
- 容量:约 1MB 限制(超过会抛出 TransactionTooLargeException)
- 数据类型:可序列化/可 Parcelable 的简单数据
- 适用场景:UI 状态标记(输入框文本、列表滚动位置、Tab 选中索引)
- 性能:有序列化开销
实际项目中的最佳实践是两种机制配合使用:
- ViewModel 持有业务数据和复杂对象
- SavedStateHandle(ViewModel 内)或
onSaveInstanceState保存关键 UI 状态 - Google 推荐使用
SavedStateHandle作为 ViewModel 的构造参数,这是因为它将两种机制统一了起来
Q2: Fragment 之间如何共享 ViewModel?
A: 使用 Activity 级别的 ViewModelStoreOwner。在 Fragment 中通过 ViewModelProvider(requireActivity()) 获取 ViewModel,两个 Fragment 拿到的将是同一个 ViewModel 实例。这种方式比传统接口回调或 EventBus 更简洁且无损(不依赖序列化/AIDL),是 Google 推荐的 Fragment 间通信方案。
实现原理:
- 每个 Fragment 有自己的 ViewModelStore(存储在 FragmentManagerViewModel 中)
- Activity 有自己的 ViewModelStore(存储在 ComponentActivity 中)
- 通过
requireActivity().viewModelStore获取 Activity 的 ViewModelStore - 所有使用此 ViewModelStore 的 Fragment 共享同一 ViewModel 实例
Kotlin 中的便捷写法:
by viewModels()— Fragment 作用域(自己的 ViewModelStore)by activityViewModels()— Activity 作用域(Activity 的 ViewModelStore)by navGraphViewModels(R.id.nav_graph)— NavGraph 作用域
Q3: ViewModel 能持有 Context 的引用吗?
A: 不能持有 Activity/Fragment 的 Context。AndroidViewModel 是 ViewModel 的子类,其构造函数接受 Application Context 引用,这个 Context 生命周期与应用进程相同,不存在泄漏风险。如果 ViewModel 需要 Context 做临时操作(如查询数据库路径),应该使用依赖注入传入 Application Context,而非 Activity 的 Context。
为什么 Activity Context 是危险的:
- ViewModel 的生命周期可能比 Activity 长(屏幕旋转后 Activity 被销毁,ViewModel 存活)
- 持有旧 Activity Context 会导致内存泄漏(GC 无法回收被 ViewModel 引用的 Activity)
- 更可怕的是,使用这个 Context 启动的 Dialog/Toast 会引用已销毁的 Activity 的 Window,导致 WindowLeaked 异常
安全的 Context 使用方式:
// 安全:Application Context |
Q4: ViewModel 的 onCleared() 何时被调用?
A: onCleared() 在 ViewModel 不再被使用且将被销毁时调用。具体触发时机:
- Activity 真正销毁(
finish()、按 Back 键退出的onDestroy),且不是配置变更(!isChangingConfigurations())时,ViewModelStore.clear()-> 对每个 ViewModel 调用clear()->onCleared()。 - Fragment 在以下情况:
- Fragment 从 back stack 中永久性移除(pop + 不添加到 back stack)
- Fragment 所在 Activity 被销毁(且不是配置变更)
- NavGraph 作用域:当对应的 NavGraph 被完全弹出 back stack 时。
注意:onCleared() 不会在配置变更时被调用,因为此时 ViewModel 仍然存活。
注意:进程被系统杀死时,onCleared() 也不会被调用(进程直接被 SIGKILL 终止)。因此不能依赖 onCleared() 来保存关键数据——应该使用 SavedStateHandle。
Q5: ViewModelProvider.Factory 的几种常用实现方式的区别?
A: ViewModel 的创建主要通过 Factory 模式。根据 ViewModel 的构造参数不同,有以下几种常用的 Factory:
无参构造函数 + 默认 Factory:
class SimpleViewModel : ViewModel() // 无参构造
// ViewModelProvider(this).get(SimpleViewModel::class.java)
// 内部使用 NewInstanceFactory,通过反射调用无参构造函数AndroidViewModel + AndroidViewModelFactory:
class MyVM(application: Application) : AndroidViewModel(application)
// 使用 ViewModelProvider.AndroidViewModelFactory.getInstance(application)
// 通过反射调用单参数(Application)构造函数SavedStateHandle + SavedStateViewModelFactory:
class MyVM(private val handle: SavedStateHandle) : ViewModel()
// ComponentActivity 默认的 Factory 同时支持 SavedStateHandle 和 Application 参数
// 它是 SavedStateViewModelFactory,内部封装了 SavedStateRegistry带自定义参数的 ViewModel + 自定义 Factory:
class MyVM(private val repo: Repository, private val userId: String) : ViewModel()
class MyFactory(private val repo: Repository, private val userId: String) :
ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return MyVM(repo, userId) as T
}
}Hilt/Dagger 注入 + @HiltViewModel(推荐):
class MyVM constructor(
private val repo: Repository,
private val savedStateHandle: SavedStateHandle
) : ViewModel()
// Hilt 在编译期生成 Factory,自动处理所有依赖注入
// 这是 Google 当前推荐的方式
核心参考 AOSP 路径:
lifecycle/viewmodel/src/main/java/androidx/lifecycle/ViewModelProvider.kt— ViewModel 获取入口lifecycle/viewmodel/src/main/java/androidx/lifecycle/ViewModelStore.java— ViewModel 存储lifecycle/viewmodel/src/main/java/androidx/lifecycle/ViewModel.java— ViewModel 基类activity/activity/src/main/java/androidx/activity/ComponentActivity.java— NonConfigurationInstances 机制fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java— Fragment ViewModelStorelifecycle/viewmodel-savedstate/src/main/java/androidx/lifecycle/SavedStateViewModelFactory.java— SavedState 集成lifecycle/lifecycle-viewmodel-ktx/src/main/java/androidx/lifecycle/ViewModel.kt— viewModelScope 扩展







