为什么需要依赖注入
一个页面要网络、要数据库、要仓库,自己 new 一堆,代码就硬编码死了。换实现、做测试都得改业务代码。依赖注入把"创建谁"这件事从业务里抽出来,交给一个容器统一管理。业务只声明要什么,容器负责给。
依赖注入的目标,是让代码面向接口而不是面向具体实现。
这篇用 Hilt 把依赖注入与模块化讲清楚。
Hilt 是什么
Hilt 是谷歌官方基于 Dagger 的安卓依赖注入框架。它预定义好 Activity、ViewModel 这些安卓组件的容器,你加几个注解就能注入,不用手写一堆 Dagger 样板。
Hilt 速查
- @HiltAndroidApp给应用开启 Hilt。
- @Inject告诉容器怎么造这个对象。
- @Module集中提供你管不到的依赖。
接入与基本用法
Application 上加 @HiltAndroidApp,需要注入的字段加 @Inject。Hilt 会在编译期把依赖关系都算清楚,缺了哪个对象直接报错,不用等到运行时才发现。
@HiltViewModel
class UserViewModel @Inject constructor(
private val repo: UserRepository
) : ViewModel()
ViewModel 的构造依赖会被 Hilt 自动准备好。
模块化提供
像 Retrofit、OkHttp 这种你不方便改源码加 @Inject 的,用 @Module + @Provides 集中提供。按模块拆分,每个功能模块管自己的依赖。
作用域与单例
全局唯一的对象用单例作用域,比如网络客户端。ViewModel 作用域随页面存活。作用域划清楚,对象不会被重复创建。
测试替换依赖
测试时用假实现替换真实仓库。因为业务面向接口,换个假的注入进去就行,业务代码一行不用动。
- 业务面向接口。
- 测试注入假实现。
- 模块各自管依赖。
实战:模块化注入仓库
在数据模块提供一个仓库实现。
@Module @InstallIn(SingletonComponent::class)
object RepoModule {
@Provides fun provideUserRepo(
api: UserApi
): UserRepository = UserRepositoryImpl(api)
}
上层 ViewModel 只声明要 UserRepository,Hilt 自动接上实现。依赖注入做到这份上,模块之间想换谁就换谁。