ViewModel生命周期,相较于Activity,Application

34 阅读4分钟

在 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(通常是 ActivityFragment,或 NavBackStackEntry)的 ViewModelStore 存活。

  • Activity 级 ViewModel:以 Activity 作为 ViewModelStoreOwner 创建(如 by viewModels() / ViewModelProvider(this)

    • 生命周期边界:从首次获取创建开始,一直存活到该 Activity 的 ViewModelStore 被清空(通常是 Activity 真正结束时)。
    • 配置变更(旋转屏幕、语言切换、夜间模式等):Activity 会重建,但同一个 ViewModel 会被复用(不会因旋转而重建)。
  • Fragment 级 ViewModel:以 Fragment 作为 owner 创建(by viewModels() / ViewModelProvider(this)

    • 生命周期边界:直到该 Fragment 的 ViewModelStore 清空(通常是 Fragment 被真正移除时)。
    • 注意:Fragment 的 ViewModel 与 Fragment 的 View 生命周期(onCreateView/onDestroyView)不同步:View 销毁不等于 ViewModel 销毁
  • 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()

ViewModel

  • 不直接感知 onResume/onPause 这类前后台回调(不以可见性为边界)
  • 关注点:
    • 跨配置变更存活
    • 在 owner 真正结束时被清理(触发 onCleared()
  • 设计目的:承载 UI 状态与业务数据,避免旋转导致数据丢失与重复请求。

3) 相较 Application:生命周期范围与职责

Application

  • 进程级别:通常跟随应用进程生命周期
  • 创建:进程启动时 Application#onCreate
  • 结束:进程被系统杀死或用户强制结束
  • 特性:
    • 全局单例、生命周期最长
    • 易被滥用为“全局状态桶”(增加耦合、难测试、易泄漏)
    • 不适合承载页面级 UI 状态

ViewModel

  • 页面/导航图级别:明显短于 Application
  • 不应持有 Activity/Fragment/View 引用(否则内存泄漏)
  • 若需要 Context:
    • 使用 AndroidViewModel(持有 Application context)
    • 或更推荐:依赖注入传入 ApplicationContext

4) 什么时候会销毁 ViewModel?

触发 ViewModel onCleared() 的典型场景:

  • owner(Activity / Fragment / NavBackStackEntry)真正结束,其 ViewModelStore 被清空
  • Activity 而言:
    • finish() 且不再重建
    • 返回键退出并从返回栈移除
  • Fragment 而言:
    • Fragment 被从 FragmentManager 中移除(注意不是仅 onDestroyView
  • Navigation 而言:
    • 对应的 NavBackStackEntry 出栈

通常不会触发 onCleared() 的场景:

  • 旋转屏幕等配置变更(ViewModel 继续存活并复用)
  • 暂时进入后台(Activity onStop 但仍在返回栈中)
  • Fragment 的 onDestroyView(仅视图销毁,VM 通常仍存活)

5) 对照表:一眼看清差异

维度ActivityViewModel(以 Activity 为 owner)Application
作用范围单个页面容器页面级状态/业务数据管理全局/进程级
配置变更通常重建通常保留复用不受影响
进入后台onStop仍存活(只要 owner 还在栈中)存活
真正退出页面onDestroyonCleared()仍存活
进程被杀销毁销毁(状态需外部恢复)销毁
适合存放UI 控制、导航、生命周期回调UI 状态、加载/缓存、业务逻辑全局初始化、全局服务入口

6) 实践建议(避免常见坑)

  • 不要在 ViewModel 里持有 Activity/Fragment/View 引用(避免内存泄漏)
  • 需要 Context 时优先用 ApplicationContext(DI 传入),或用 AndroidViewModel
  • 需要跨进程死亡恢复数据:
    • SavedStateHandle(适合小状态)
    • 或持久化:Room / DataStore / SharedPreferences 等
    • 结论:ViewModel 只保证 跨配置变更,不保证 进程死亡后恢复