当前位置: 首页 > news >正文

Android ViewModel机制与底层原理详解

Android 的 ViewModel 是 Jetpack 架构组件库的核心部分,旨在以生命周期感知的方式存储和管理与 UI 相关的数据。它的核心目标是解决两大痛点:

  1. 数据持久化: 在配置变更(如屏幕旋转、语言切换、多窗口模式切换)时保留数据,避免重新加载数据造成的资源浪费和用户体验中断。
  2. 职责分离: 将 UI 控制器(Activity/Fragment)与数据操作逻辑分离,使代码更清晰、可测试性更强。

核心机制与原理详解

  1. 设计目标与核心思想:

    • 生命周期感知: ViewModel 对象的生命周期比其关联的 UI 控制器(Activity/Fragment)更长。它从 UI 控制器创建开始,直到其关联的 UI 控制器永久销毁Activity finish()Fragment 分离且不再附加)时才被销毁。这意味着配置变更导致的临时销毁与重建不会影响 ViewModel
    • 数据持有者: 主要负责持有、准备和管理 UI 所需的数据。它可以执行数据获取(如从数据库、网络)、转换、聚合等操作。
    • UI 控制器解耦: UI 控制器(Activity/Fragment)负责显示数据和响应用户交互,ViewModel 负责提供数据和处理业务逻辑。两者通过观察(如 LiveData)或直接调用接口进行通信。
  2. 创建过程:

    • 入口点: 通常使用 ViewModelProvider 来获取 ViewModel 实例。
    // 在 Activity 中
    val viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
    // 在 Fragment 中 (推荐使用 activityViewModels 或 viewModels)
    val viewModel: MyViewModel by viewModels()
    val sharedViewModel: SharedViewModel by activityViewModels()
    
    • 关键参数 - ViewModelStoreOwner: ViewModelProvider 的构造函数需要一个 ViewModelStoreOwnerComponentActivity (AppCompatActivity 的基类) 和 Fragment 都实现了这个接口。它提供了访问 ViewModelStore 的能力。
    • ViewModelProvider 的作用:
      • 检查 ViewModelStore 中是否已存在请求类型的 ViewModel 实例。
      • 如果存在,直接返回该实例。
      • 如果不存在,则通过 Factory(默认为 NewInstanceFactoryAndroidViewModelFactory)创建新实例,并将其存储在 ViewModelStore 中,然后返回。
  3. 存储与作用域 - ViewModelStore

    • 核心容器: 每个 ViewModelStoreOwner(如一个 Activity 或一个特定 Fragment)内部都维护着一个 ViewModelStore。它是一个简单的容器类(通常是 HashMap<String, ViewModel>),负责存储与该作用域关联的所有 ViewModel 实例。
    • 键 (Key): ViewModelViewModelStore 中的存储键通常是其类名(如 "com.example.MyViewModel")。当使用带 Factory 的特定键时(如为同一类型创建多个实例),键会更复杂。
    • 配置变更下的存活: 当配置变更发生时,系统销毁并重建 UI 控制器 (Activity/Fragment)。但是,系统会将 ViewModelStore 对象保留在内存中。重建后的新 UI 控制器实例会重新附加到同一个 ViewModelStore。因此,ViewModelProvider 能在新 UI 控制器中检索到之前创建的 ViewModel 实例。
    • 永久销毁: 当 UI 控制器真正结束其生命周期(用户按返回键、调用 finish()Fragment 被永久移除),系统会调用 ViewModelStoreclear() 方法。该方法会遍历所有存储的 ViewModel 实例,调用它们的 onCleared() 方法(用于释放资源,如取消异步任务),然后清空 Map。之后,ViewModelStore 及其包含的 ViewModel 实例会被垃圾回收。
  4. 与生命周期的绑定 - Lifecycle

    • 自动关联: 当你通过 ViewModelProvider(owner) 创建 ViewModel 时,该 ViewModel 就自动与 owner (ViewModelStoreOwner) 的生命周期关联起来了。
    • onCleared() 钩子: ViewModel 类提供了一个 onCleared() 方法。当关联的 ViewModelStoreclear() 时(即 UI 控制器永久销毁时),框架会自动调用这个方法。开发者可以重写此方法来清理资源(如取消正在进行的网络请求、关闭数据库连接、移除监听器等)。这是 ViewModel 感知其“结束”生命周期的关键点。
  5. 数据通信 (通常结合 LiveData):

    • 最佳搭档: 虽然 ViewModel 可以包含任何数据,但 LiveData 是其推荐的用于向 UI 暴露数据的方式。
    • 机制: ViewModel 内部持有 LiveData 对象(通常是 MutableLiveData 私有,暴露为 LiveData 公有)。UI 控制器 (Activity/Fragment) 在 onCreate()onViewCreated() 中观察这些 LiveData
    • 优势:
      • 生命周期感知订阅: LiveData 自动管理订阅,确保只在 UI 控制器处于活跃状态 (STARTEDRESUMED) 时才更新 UI,避免在后台更新导致的崩溃或资源浪费。
      • 数据更新: ViewModel 中的业务逻辑(如响应按钮点击的网络请求)完成后,通过更新 MutableLiveData 的值来触发 LiveData 通知观察者(UI 控制器)。
      • 配置变更无缝衔接: 由于 ViewModelLiveData 在配置变更后存活,新的 UI 控制器重新观察同一个 LiveData 时,会立即收到最后一次保存的数据,从而实现无缝恢复。
  6. 作用域扩展 (SavedStateHandle):

    • 需求: 基本 ViewModel 在进程被系统杀死后重建时,其内部数据也会丢失。需要一种机制在进程死亡后恢复少量关键 UI 状态(如列表滚动位置、输入框临时内容)。
    • 解决方案: ViewModel 库提供了 SavedStateHandle 作为 ViewModel 构造函数的参数。
    • 原理:
      • 当使用 SavedStateHandle 时,ViewModel 的创建工厂(如 AbstractSavedStateViewModelFactory)会负责将 SavedStateHandle 注入到 ViewModel 中。
      • SavedStateHandle 本质上是一个键值对容器 (Map<String, Any?>)。它利用底层 ActivityonSaveInstanceState(Bundle) 机制。
      • 在 UI 控制器临时销毁(配置变更)或可能永久销毁(进程回收)前,SavedStateHandle 中的数据会被序列化到 Bundle 中。
      • 在 UI 控制器重建后,Bundle 中的数据会被反序列化回 SavedStateHandle。这样,即使在进程被杀死后重建,ViewModel 也能通过 SavedStateHandle 恢复那些关键状态。
    • 使用:
      class MyViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {val someState: MutableStateFlow<String> = savedStateHandle.getStateFlow("key", "")// 或者使用 LiveDataval liveDataState: LiveData<String> = savedStateHandle.getLiveData("key")fun updateState(newValue: String) {savedStateHandle["key"] = newValue // 自动触发保存}
      }
      // 创建时需要使用 SavedStateViewModelFactory 或 by viewModels() 自动处理
      
  7. Fragment 间共享数据:

    • 场景: 同一个 Activity 下的多个 Fragment 需要共享数据(如购物车、用户资料)。
    • 实现: 让这些 Fragment 使用 同一个作用域ViewModelStoreOwner。通常,这个共享的作用域就是它们所属的 Activity
    • 方法:Fragment 中,使用 activityViewModels() 委托或 ViewModelProvider(requireActivity()) 来获取 ViewModel 实例。
    • 原理: 所有通过该 Activity 作用域 (ViewModelStoreOwner) 获取的同一类型的 ViewModel(使用默认 Key),返回的都是同一个实例。因此,不同的 Fragment 访问的是同一个 ViewModel 对象,自然就实现了数据共享和通信。
  8. 底层关键类与交互:

    • ViewModel: 开发者继承的基类,包含数据和逻辑,有 onCleared() 钩子。
    • ViewModelStoreOwner: 接口(ComponentActivity, Fragment 实现),提供 getViewModelStore() 方法。
    • ViewModelStore: 内部维护一个 Map<String, ViewModel>,负责存储和清理 ViewModel
    • ViewModelProvider: 工厂类,负责从 ViewModelStore 获取或创建 ViewModel 实例。
    • ViewModelProvider.Factory: 接口,用于创建 ViewModel 实例(支持带参数构造函数)。
    • SavedStateHandle: 用于在进程死亡后恢复少量状态的辅助类。
    • ComponentActivity: 实现了 ViewModelStoreOwnerHasDefaultViewModelProviderFactory,在其 onRetainNonConfigurationInstance() 中保存 ViewModelStore,在 onCreate() 中恢复。在其 onDestroy() 中判断是否为配置变更决定是否调用 ViewModelStore.clear()
    • FragmentManagerViewModel: (Fragment 作用域实现的关键) 一个特殊的 ViewModel,由 FragmentManager 持有,用于管理 Fragment 作用域的 ViewModelStore 以及嵌套 Fragment 的作用域关系。
  9. 重要注意事项:

    • 绝不持有 View/Activity Context 引用: ViewModel 生命周期可能比 Activity 长。如果持有 Activity 引用,会导致 Activity 无法被回收,造成内存泄漏。如果需要 Application Context,使用 AndroidViewModel(它持有 Application 引用,Application 生命周期等同于进程)。
    • 轻量级状态恢复: SavedStateHandle 用于恢复少量、序列化/反序列化快的 UI 相关状态。不要用它存储大量数据或复杂对象。大数据应持久化到数据库或网络。
    • 异步操作:ViewModel 中启动的异步操作(如协程、LiveData 转换),必须在 onCleared() 中取消或清理,防止内存泄漏和无效更新。
    • 测试友好: 由于 ViewModel 不依赖 Android 框架的具体 UI,它们可以非常方便地在 JUnit 测试中进行单元测试。

总结流程图

(配置变更 / 进程重建)|v
+-------------------+
| UI Controller      | (Activity/Fragment) 销毁或重建
| (onDestroy)       | --(永久销毁?)--> Yes -> [调用 ViewModelStore.clear()] -> [触发 ViewModel.onCleared()]
|                   | --(配置变更?)--> Yes -> [系统保留 ViewModelStore]
+-------------------+|| (重建后)v
+-------------------+
| UI Controller      | (新的 Activity/Fragment 实例)
| (onCreate)        | --[创建 ViewModelProvider] --> [请求 ViewModel]
+-------------------+|v
+-------------------+
| ViewModelProvider | --[检查 ViewModelStore] --> [存在?] -> Yes -> 返回现有实例
|                   |                          --> No  -> [使用 Factory 创建新实例] -> [存入 ViewModelStore] -> 返回实例
+-------------------+|v
+-------------------+
| ViewModel         | --[持有 LiveData/SavedStateHandle] --> [提供数据/处理逻辑]
|                   | <--[观察 LiveData / 调用方法]------- UI Controller
+-------------------+|v
(UI 显示数据/响应用户交互)

ViewModel 的核心在于 ViewModelStore 在配置变更中的持久性,以及其生命周期与 UI 控制器的解耦(存活至永久销毁)。结合 LiveData 的生命周期感知数据观察和 SavedStateHandle 的轻量级状态持久化,它构成了 Android 现代、健壮、可测试的 UI 架构基石。理解 ViewModelStoreOwnerViewModelStoreViewModelProvider 的协作机制是掌握其底层原理的关键。

http://www.lryc.cn/news/583047.html

相关文章:

  • upload-labs靶场通关详解:第19关 条件竞争(二)
  • 池化思想-Mysql异步连接池
  • 5.注册中心横向对比:Nacos vs Eureka vs Consul —— 深度解析与科学选型指南
  • Web 前端框架选型:React、Vue 和 Angular 的对比与实践
  • 华为静态路由配置
  • 小米路由器3C刷OpenWrt,更换系统/变砖恢复 指南
  • 语音识别核心模型的数学原理和公式
  • 从互联网电脑迁移Dify到内网部署Dify方法记录
  • 【编程史】IDE 是谁发明的?从 punch cards 到 VS Code
  • 计算机网络实验——访问H3C网络设备
  • Java项目集成Log4j2全攻略
  • Using Spring for Apache Pulsar:Publishing and Consuming Partitioned Topics
  • 飞算 JavaAI 智能编程助手 - 重塑编程新模态
  • bash 判断 /opt/wslibs-cuda11.8 是否为软连接, 如果是,获取连接目的目录并自动创建
  • (C++)任务管理系统(正式版)(迭代器)(list列表基础教程)(STL基础知识)
  • `fatal: bad config value for ‘color.ui‘`错误解决方案
  • ali linux 安装libreoffice
  • Markdown入门
  • 类和对象拓展——日期类
  • Django核心知识点详解:JSON、AJAX、Cookie、Session与用户认证
  • npu-smi info 华为昇腾NPU 状态监控工具解读
  • 类与对象【下篇】-- 关于类的其它语法
  • 树莓派vsftpd文件传输服务器的配置方法
  • 【02】MFC入门到精通——MFC 手动添加创建新的对话框模板
  • overleaf 改为XeLatex
  • Vue响应式原理四:响应式-监听属性变化
  • 正点原子学习 用户权限管理
  • 【python基础】运算符与布尔值全解析
  • 智慧航天运载体系全生命周期监测 | 图扑数字孪生
  • Shader面试题100道之(41-60)