第1章:Hilt与ViewModel集成原理
理解Hilt与ViewModel的集成原理,是构建可维护、可测试Android应用的基石。本章从依赖注入的核心动机出发,剖析ViewModel的生命周期特性,并深入Hilt的组件层次结构,阐明这三者如何协同工作以实现“零模板代码”的依赖注入。
1.1 依赖注入核心概念
依赖注入(DI)通过将对象的创建与消费分离,消除硬编码依赖。在Android开发中,手动管理依赖会导致大量工厂类、单例模式滥用以及测试困难。Hilt基于Dagger,在编译期生成高效、可溯源的注入代码。
| 概念 | 说明 |
|---|---|
| 依赖 | 一个对象需要协作的其他对象(如Repository需要RemoteDataSource) |
| 注入器 | 负责提供依赖实例的组件(在Hilt中由@Component注解的接口表示) |
| 绑定 | 将抽象类型映射到具体实现(通过@Module + @Binds 或 @Provides) |
| 作用域 | 控制依赖实例的生命周期范围(如@Singleton、@ViewModelScoped) |
关键洞察:Hilt将DI从“运行时手动组装”提升为“编译期声明式注入”,ViewModel不再需要关心自己的依赖来自哪里。
1.2 ViewModel生命周期
ViewModel必须比其宿主(Activity/Fragment)更长寿,以承受配置变更(如屏幕旋转)。标准方式是通过ViewModelProvider获取实例,但若ViewModel的构造函数需要参数,就需要自定义ViewModelFactory。Hilt通过@HiltViewModel注解和SavedStateHandle自动处理这一痛点。
// 标准手工工厂方式(繁琐)
class MyViewModelFactory(
private val repo: MyRepository
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return MyViewModel(repo) as T
}
}
// Hilt 自动注入方式(简洁)
@HiltViewModel
class MyViewModel @Inject constructor(
private val repo: MyRepository,
private val savedStateHandle: SavedStateHandle
) : ViewModel()
生命周期对齐:Hilt保证注入到ViewModel中的依赖在ViewModel被清除时一并释放,无需手动取消订阅。
1.3 Hilt组件层次
Hilt定义了一套以Android生命周期为驱动的组件层次结构。每个组件都有明确的创建与销毁时机,并可以访问其父组件的绑定。ViewModel的注入发生在ActivityRetainedComponent(由@ViewModelScoped管理)或ActivityComponent级别。
// 指定注入发生在 ViewModel 宿主 Activity 的组件中
@Module
@InstallIn(ActivityComponent::class)
object NetworkModule {
@Provides
fun provideApiService(): ApiService = Retrofit.Builder()
.baseUrl("https://api.example.com")
.build()
.create(ApiService::class.java)
}
// 使用 @ViewModelScoped 让依赖在 ViewModel 生命周期内保持单例
@Module
@InstallIn(ActivityRetainedComponent::class)
object RepoModule {
@Provides
@ViewModelScoped
fun provideMyRepository(api: ApiService): MyRepository {
return MyRepository(api)
}
}
1.4 集成关键流程
- 声明依赖:使用
@Module定义如何提供依赖,并用@InstallIn指定组件层级。 - 注入目标:在ViewModel构造函数上添加
@Inject constructor,并用@HiltViewModel标注类。 - 宿主激活:Activity/Fragment使用
@AndroidEntryPoint,Hilt自动生成Factory并绑定到生命周期。 - 获取实例:通过
by viewModels()或hiltViewModel()扩展函数获取ViewModel。
常见错误:如果在ViewModel中注入一个@ActivityScoped的依赖,该依赖会跟随Activity生命周期,而非ViewModel。应优先使用@ViewModelScoped或@Singleton来保证生命周期对齐。
// 正确实践:依赖生命周期 <= ViewModel
@Module
@InstallIn(ActivityRetainedComponent::class)
object UserModule {
@Provides
@ViewModelScoped
fun provideUserManager(): UserManager = UserManager()
}
// 使用
@HiltViewModel
class ProfileViewModel @Inject constructor(
private val userManager: UserManager,
private val savedStateHandle: SavedStateHandle
) : ViewModel()
通过本章的剖析,依赖注入不再是黑盒:它是一个声明式、编译期可验证、生命周期感知的优雅架构。下一站我们将通过Hello World项目,亲手实践Hilt与ViewModel的第一次集成。