Hilt
Hilt 是 Android 专属的依赖注入 (Dependency Injection, DI) 库,它建立在著名的 Dagger 库之上,旨在简化 Dagger 在 Android 应用中的使用。它通过提供一套标准的组件和作用域,并自动生成和管理它们所需的模板代码,让开发者更容易地实现依赖注入。
1、为什么需要 Hilt?(解决的问题)
在没有依赖注入的情况下,一个类(如 ViewModel 或 Activity)可能需要自己创建它所依赖的对象。这会导致:
代码耦合度高:类与它的依赖紧密绑定,难以单独测试和替换。
难以测试:无法轻松传入模拟对象(Mock)进行单元测试。
样板代码多:手动创建和管理依赖对象的生命周期非常繁琐。
Hilt 通过以下方式解决了这些问题:
解耦:将依赖项的创建与使用它的类分离开。
可测试性:可以轻松地为测试提供不同的实现(例如,将网络模块替换为模拟模块)。
生命周期管理:Hilt 自动管理依赖对象的生命周期,使其与 Android 组件(如 Activity、Fragment)的生命周期对齐。
减少样板代码:通过注解自动生成代码,开发者只需声明依赖关系。
2、Hilt 的核心概念
要理解 Hilt,必须掌握以下几个核心概念:
a. 依赖注入 (Dependency Injection)
一种设计模式,一个对象接收它所需要的其他对象(即它的“依赖”),而不是自己创建它们。例如:
// 没有 DI
class MyViewModel {
private val repository = MyRepository() // 自己创建依赖,耦合紧密
}
// 有 DI
class MyViewModel(
private val repository: MyRepository // 依赖从外部“注入”进来
)
b. @Inject 注解
用于告知 Hilt 如何提供某个类型的实例,以及标记需要被注入依赖的字段或构造函数。
构造函数注入:在类的构造函数上使用 @Inject,告诉 Hilt 如何创建该类的实例。
class MyRepository @Inject constructor() {
// ...
}
字段注入:在 Android 组件(如 Activity、Fragment)的属性上使用 @Inject,告诉 Hilt 需要为此字段提供依赖。
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var myRepository: MyRepository // Hilt 会自动注入这个字段
}
c. Hilt 模块 (@Module 和 @Provides)
当无法使用构造函数注入时(例如,需要注入接口、第三方库类或需要通过 builder 创建的对象),就需要使用 Hilt 模块。
@Module:标注一个类,告诉 Hilt 这是一个提供依赖的模块。
@Provides:标注模块类中的一个函数,告诉 Hilt 这个函数是提供某种类型实例的方法。
@Module
@InstallIn(SingletonComponent::class) // 告诉 Hilt 这个模块安装在哪个组件中
object NetworkModule {
@Provides
@Singleton // 单例作用域
fun provideOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder().build()
}
// 假设 ApiService 是一个接口,无法用 @Inject 构造
@Provides
fun provideApiService(okHttpClient: OkHttpClient): ApiService {
return Retrofit.Builder()
.baseUrl("https://example.com/")
.client(okHttpClient)
.build()
.create(ApiService::class.java)
}
}
d. Hilt 组件 (Component) 和作用域 (Scope)
Hilt 预定义了一系列与 Android 生命周期相关的组件,每个组件都有对应的作用域注解。
组件:Hilt 容器,负责将依赖注入到对应的 Android 类中。每个组件都有一个生命周期。
作用域:将依赖的生命周期限定在某个组件的生命周期内。例如,一个被 @Singleton 标注的依赖在整个应用生命周期内只有一个实例。
| Android 类 |
生成的 Hilt 组件 |
作用域 |
| Application |
SingletonComponent |
@Singleton |
| Activity |
ActivityComponent |
@ActivityScoped |
| Fragment |
FragmentComponent |
@FragmentScoped |
| View |
ViewComponent |
@ViewScoped |
| 带有 @WithFragmentBindings 的 View |
ViewWithFragmentComponent |
@ViewScoped |
| Service |
ServiceComponent |
@ServiceScoped |
如何使用:在模块上使用 @InstallIn 指定它属于哪个组件,在 @Provides 方法或可注入的类上使用作用域注解来限定生命周期。
@Module
@InstallIn(ActivityComponent::class) // 这个模块安装在 Activity 组件中
object MyActivityModule {
@Provides
@ActivityScoped
fun provideMyActivityScopedDep(): MyDep { ... }
}
e. @AndroidEntryPoint
标注一个 Android 组件(如 Activity, Fragment, View, Service, BroadcastReceiver),表示这个类是 Hilt 的入口点,Hilt 会为它生成所需的组件,并允许在其中进行字段注入。
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var viewModel: MyViewModel
// ...
}
3. Hilt 与 ViewModel
Hilt 对 ViewModel 提供了专门的支持,使得注入 ViewModel 变得非常简单。
1、添加依赖:确保添加了 hilt-androidx-viewmodel 依赖。
2、使用 @HiltViewModel:在 ViewModel 的构造函数上使用 @HiltViewModel 和 @Inject。
@HiltViewModel
class MyViewModel @Inject constructor(
private val savedStateHandle: SavedStateHandle, // Hilt 自动提供
private val repository: MyRepository // 你的自定义依赖
) : ViewModel() {
// ...
}
3、在 Activity/Fragment 中获取:使用普通的 by viewModels() 委托属性来获取 ViewModel 实例,Hilt 会自动处理依赖注入。
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
// 无需 @Inject,使用 viewModels() 委托
private val viewModel: MyViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// viewModel is ready to use
}
}
4. 重要注解一览(常用并解释)
@HiltAndroidApp :标注在 Application 上,触发 Hilt 的代码生成与顶级组件(必须有)。
@AndroidEntryPoint:标注 Activity/Fragment/Service/View/Receiver 等,允许成员注入(@Inject 字段)。
@HiltViewModel:标注ViewModel,配合@Inject 构造器让 Hilt 提供 ViewModel。可与 hiltViewModel()(Compose)或 by viewModels() + Hilt 集成使用。
@Module/@InstallIn(...) :定义绑定/提供函数并指定安装到哪个 Component。
@Provides/@Binds :提供实例与接口绑定(@Binds 更轻量但要求接口实现类以参数形式传入)。
@Qualifier / @Named:用于区分同类型的多个绑定。
@Singleton、、@ActivityScoped、@ViewModelScoped 等:绑定实例生命周期。