ViewModel 原理
一、ViewModel 核心定位
- 一句话定义:ViewModel 是一个生命周期感知的 UI 数据容器,负责准备和管理 Activity/Fragment 所需的数据,并在配置变更(如屏幕旋转)时保留数据。
- 解决问题:避免因 Activity 重建导致数据丢失,将数据管理逻辑从 UI 控制器中解耦,提升代码可读性与可测试性。
二、基本使用流程
1. 创建 ViewModel
class MyViewModel : ViewModel() {
var data: String = ""
}
2. 在 Activity/Fragment 中获取
class MainActivity : AppCompatActivity() {
private val viewModel: MyViewModel by viewModels() // 或 ViewModelProvider(this).get(...)
override fun onCreate(savedInstanceState: Bundle?) {
// 直接使用 viewModel.data
}
}
面试要点:
- 使用
ViewModelProvider(this).get(MyViewModel::class.java)获取实例,确保同一个 Activity 内多次获取得到同一对象。 - KTX 扩展
by viewModels()简化写法,背后原理相同。
三、工作原理与核心源码走读

1. 涉及的核心类与职责
| 类/接口 | 作用 |
|---|---|
ViewModelStoreOwner |
接口,拥有 ViewModelStore,实现类如 ComponentActivity、Fragment。 |
ViewModelStore |
内部用 HashMap<String, ViewModel> 缓存 ViewModel 实例。 |
ViewModelProvider |
工具类,结合 ViewModelStore 和 Factory 获取或创建 ViewModel。 |
Factory |
负责实例化 ViewModel,默认 NewInstanceFactory 通过反射调用无参构造。 |
2. ViewModel 获取流程(结合源码)
关键细节:
- 存储 Key 为
DEFAULT_KEY + ":" + 类全限定名,保证同一作用域内唯一。 - 第一次获取时通过
Factory反射创建,之后直接从ViewModelStore返回,确保同一对象。
直接背诵版:
ViewModel 的获取流程,核心是在 ViewModelProvider 的 get() 方法里完成的,可以概括为“先查缓存,没有就创建,创建完再缓存”。我按源码调用链说一下:
-
入口
ViewModelProvider(this).get(MyViewModel.class)- 首先会通过ViewModelProvider 的构造函数,依靠传进来的Activity或Fragment 来得到对应的
ViewModelStore - 最终会调用到
get(key, modelClass),其中key是DEFAULT_KEY + ":" + 类全限定名,确保同一个作用域内 ViewModel 的唯一性。
- 首先会通过ViewModelProvider 的构造函数,依靠传进来的Activity或Fragment 来得到对应的
-
第一步——查缓存
先通过mViewModelStore.get(key)去ViewModelStore里取。ViewModelStore内部就是一个HashMap<String, ViewModel>。- 如果能取到,且类型匹配,就直接返回,不会重新创建。
-
第二步——创建实例
如果缓存没命中,就通过Factory去创建。- 默认的
Factory是NewInstanceFactory,底层直接用modelClass.newInstance()反射调用无参构造。 - 如果是
AndroidViewModel,会使用AndroidViewModelFactory,反射调用带Application参数的构造器。
- 默认的
-
第三步——存入缓存
创建成功后,立即调用mViewModelStore.put(key, viewModel)存起来。put时会检查是否有旧值,若有则先调旧值的onCleared(),再放入新实例。- 最后返回这个新建的 ViewModel。
补充一点:为什么同一个 Activity 内多次获取是同一个实例?
因为 ViewModelStore 是 Activity 级别的单例容器,Activity 没有真正销毁时,ViewModelStore 一直存在,所以通过同一个 key 拿到的总是同一个 ViewModel。
3. 为什么配置变更(屏幕旋转)时 ViewModel 不销毁?
核心答案:ViewModelStore 被保留下来,因为 Activity 的 onRetainNonConfigurationInstance() 将 ViewModelStore 缓存到了系统非配置实例中,重建后通过 getLastNonConfigurationInstance() 取回。
- 关键方法:
ComponentActivity的getViewModelStore()优先从getLastNonConfigurationInstance()恢复mViewModelStore。 - 清理时机:只有
ON_DESTROY且 非配置变更(!isChangingConfigurations())时,才会调用ViewModelStore.clear(),进而调用ViewModel.onCleared()释放资源。
最关键的就一句话:
ViewModel 存在 ViewModelStore 里,而 ViewModelStore 在屏幕旋转时被系统暂存,重建 Activity 后又原样取回来,所以里面的 ViewModel 实例没被销毁。
疑问:Activity 对象被销毁了,为什么它生成的数据NonConfigurationInstances还存在?
在 Android 框架层,系统会在销毁旧 Activity 之前,主动调用 onRetainNonConfigurationInstance() 获取数据,并将其缓存到 ActivityClientRecord(位于 ActivityThread 中),与 Activity 实例本身的销毁无关。新 Activity 重建后,系统再将这份数据注入回去。
每个 Activity 实例拥有自己的 ViewModelStore,每个 Fragment 实例也拥有自己的 ViewModelStore,互不干扰。
Activity、Fragment 实现了
ViewModelStoreOwner这个接口, 唯一抽象方法getViewModelStore()是获取当前实例持有的的ViewModelStore
4. ViewModel 的 onCleared() 方法
ViewModel抽象类中定义了onCleared(),子类可重写用于清理资源(如取消协程、注销监听)。- 被调用时机:当 Activity 真正 finish(非配置变更),
ViewModelStore.clear()遍历所有 ViewModel 并调用其clear()->onCleared()。
5. AndroidViewModel 与 ViewModel
AndroidViewModel继承ViewModel,内部持有Application引用,方便在 ViewModel 中获取应用级上下文,避免内存泄漏(不持有 Activity)。- 实例化时需传入
Application,通过AndroidViewModelFactory使用带参构造器反射创建。
面试常用说法:如果需要 Context,优先使用 AndroidViewModel,因为它的生命周期长于 Activity,不会导致泄漏。
四、面试常见问题速答
Q1:ViewModel 和 onSaveInstanceState 的区别?
- ViewModel:存储复杂对象、存活于内存,配置变更不丢失,但进程被杀死后数据会丢失。
- onSaveInstanceState:保存少量、可序列化的轻量数据,适合系统杀死进程后的恢复,会被写入磁盘。通常两者结合使用。
Q2:能否在 ViewModel 中持有 Activity 引用?
绝对不能。会导致内存泄漏,因为 ViewModel 的生命周期比 Activity 长。需要 Context 请用 AndroidViewModel 获取 Application。
Q3:如何在 Fragment 间共享 ViewModel?
使用 Activity 范围的 ViewModelProvider:ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
这样多个 Fragment 访问的同一 ViewModel 实例。核心是让它们使用同一个 ViewModelStore
requireActivity()就是拿宿主 Activity 实例,同时也是进入 Activity 作用域 ViewModel 的“钥匙”。
Q4:ViewModel 的 Factory 有什么作用?
当 ViewModel 需要带参构造时(如传入 Repository 或其他依赖),自定义 Factory 来创建实例。默认无参构造器通过反射无法传递参数。
Q5:为什么 ViewModel 内部通常用 LiveData 而不是普通变量?
为了让 UI 能够观察到数据变化并自动更新,符合响应式编程,同时 LiveData 尊重生命周期,防止后台更新已销毁的 UI。
五、记忆要点总结
- 四个角色:ViewModelStoreOwner → ViewModelStore → ViewModelProvider.Factory → ViewModel
- 获取流程:检查 Store 缓存 → 无则 Factory 创建 → 放入 Store → 返回实例
- 旋转不销毁:
onRetainNonConfigurationInstance()保留 ViewModelStore,重建时取回 - 清理规则:仅当 Activity 真正 finish(非配置变更)才清除
- 最佳搭档:LiveData(数据驱动 UI) + Repository(数据层解耦)
- 安全使用 Context:优先继承
AndroidViewModel使用 Application
参考: https://blog.csdn.net/sytregn/article/details/138801493
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)