Android中@Inject与by viewModels()获取ViewModel的核心差异及理解验证
关于ViewModel两种获取方式的理解纠正与说明
你的理解有部分正确,但存在关键误区,下面逐一拆解:
一、by viewModels()的理解完全正确
by viewModels()是Jetpack官方提供的Kotlin扩展,核心逻辑是依托宿主(Activity/Fragment)的ViewModelStore来缓存实例:
- 只要宿主没被彻底销毁(比如仅发生屏幕旋转这类配置变更),每次调用都会返回同一个ViewModel实例;
- 它天然遵循ViewModel的生命周期规则,配置变更时实例会被保留,直到宿主真正退出才会被清理。
你的代码示例是标准用法:
val viewModel: BookMarkImageViewModel by viewModels()
二、@Inject获取ViewModel的理解有偏差
你提到“未加@Singleton时@Inject每次都会创建新实例”,这个说法不准确,核心问题在于ViewModel的实例管理要同时兼顾Dagger作用域和ViewModel的生命周期规范:
1. 直接@Inject是错误用法
你写的这种直接注入方式:
@Inject lateinit var viewModel: BookMarkImageViewModel
本质上是把ViewModel当成了普通依赖,它不受ViewModelStore管理,完全失去了ViewModel的生命周期特性:
- 如果Dagger中给这个ViewModel没配置任何作用域,每次注入确实会生成新实例;
- 就算加了@Singleton,虽然能拿到全局单例,但会导致ViewModel脱离宿主生命周期,引发内存泄漏或多页面状态混乱(比如多个Activity共享同一个ViewModel)。
2. 正确的Dagger注入ViewModel姿势
正确的做法是通过ViewModelProvider.Factory结合Dagger注入,让ViewModel同时受Dagger作用域和ViewModelStore管控:
- 给ViewModel绑定与宿主生命周期匹配的作用域(比如Jetpack提供的
@ActivityRetainedScope,这个作用域会跨配置变更保留实例),此时@Inject会在宿主生命周期内返回同一个实例,效果和by viewModels()完全一致; - 根本不需要用@Singleton,因为ViewModel的生命周期应该和宿主绑定,而非全局单例。
总结
by viewModels()是ViewModel的标准用法,你的理解没问题;@Inject的行为取决于配置:直接注入是错误用法,正确结合ViewModelFactory和匹配作用域后,也能实现生命周期绑定的实例复用,并非必须加@Singleton才会复用。
内容的提问来源于stack exchange,提问作者user9628944
相关产品推荐
相关产品推荐

