1. Hilt 简介:依赖注入基础与核心概念
依赖注入(DI)是什么?说白了就是 "我不要自己 new,你帮我传进来"。我刚开始写 Android 那会,一个 Activity 里能写三四个 ViewModelProvider,再加一堆 Retrofit、OkHttp 的初始化,代码又长又乱。后来用 Dagger 吧,配置又太重。直到 Hilt 出来,嗯,算是找到了一个顺手的工具。
Hilt 是 Google 基于 Dagger 封装的 Android 专用依赖注入框架。它帮你省掉了 Dagger 里那些繁琐的 Component、Module 绑定,让你只需要写几个注解,就能让对象自动注入进来。我把它叫做 "带路标的 Dagger" —— 核心功力还是 Dagger,但路标都给你画好了。
依赖注入的三个角色
要理解 Hilt,得先明白依赖注入到底在干嘛。我这里用一个日常的例子来说:
- 客户(Client):需要某个服务,比如一个网络请求客户端
- 服务(Service):具体干活的对象,比如 OkHttpClient
- 注入器(Injector):负责把服务组装好交给客户
在没 DI 的时候,客户自己 new OkHttpClient(),耦合死了。用了 DI,客户只管声明 @Inject lateinit var client,剩下都交给框架。
核心思想:控制反转(IoC)—— 不是你主动去拿依赖,而是依赖主动送上门来。
Hilt 的核心概念
我习惯把 Hilt 的核心组件分为三级来记:
| 概念 | 作用 | 常用注解 |
|---|---|---|
| 容器 | 管理对象的创建与生命周期 | @HiltAndroidApp, @AndroidEntryPoint |
| 绑定 | 告诉容器怎么创建对象 | @Inject, @Module, @Provides |
| 作用域 | 控制对象的存活范围 | @Singleton, @ActivityScoped |
举个例子,你在 Application 上加上 @HiltAndroidApp,Hilt 就会在编译期生成一个「父容器」。每个加了 @AndroidEntryPoint 的 Activity 会生成一个「子容器」。这样一来,你可以在 Activity 里直接 @Inject 拿到 Retrofit 实例,而且还能控制它是全局单例还是跟随 Activity 销毁。
一张图看懂 Hilt 的组件层级
下面这张图是我在项目里经常用来给新人解释的,把 Hilt 的容器分层结构画清楚了:
我的建议:刚开始不要纠结所有注解。先记住 @HiltAndroidApp 和 @AndroidEntryPoint 这两个入口注解,再配合 @Inject 声明依赖,就能跑起来。我第一个 Hilt 项目就是这么干的,代码清爽多了。
快速入门:三步走
- 加依赖:在
build.gradle(project) 里添加 Hilt 插件,在模块build.gradle里引用hilt-android和hilt-compiler。 - 打入口:在 Application 类上加
@HiltAndroidApp,在 Activity 上加@AndroidEntryPoint。 - 写注入:在 Activity 里用
@Inject lateinit var repo声明你需要的东西,然后在需要的地方直接使用。
// Application
@HiltAndroidApp
class MyApp : Application()
// Activity
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var userRepository: UserRepository
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 现在 userRepository 已经被注入了
}
}
注意:我曾经犯过一个低级错误 —— 在 @AndroidEntryPoint 的 Activity 里直接 val repo = UserRepository(),导致 Hilt 没起作用。记住,@Inject 注解的字段只能在 super.onCreate() 之后才可用,因为那时 DI 容器才初始化完毕。
核心概念速查表
| 注解 | 用途 | 使用位置 |
|---|---|---|
@HiltAndroidApp |
触发 Hilt 代码生成 | Application 类 |
@AndroidEntryPoint |
启用依赖注入 | Activity、Fragment、Service、BroadcastReceiver |
@Inject |
标记需要注入的字段或构造函数 | 字段、构造函数、方法 |
@Module |
定义如何提供依赖 | 类 (搭配 @InstallIn) |
@Provides |
标注 Module 中的方法 | Module 类的方法 |
@Singleton |
标记全局单例 | 类或 provide 方法 |
你可能会问:这么多注解,怎么记得住?我个人的经验是:先跑通一个最简单的例子,然后每次遇到 "依赖找不到" 的编译错误时,再去查对应的注解该怎么写。Hilt 的错误提示其实挺人性化的,会直接告诉你是少了 @Inject 还是缺了 Module。
好,这一章我们先把概念铺好。下一阶段,我们会实战写一个完整的网络请求诊断工具,到时候你会发现,Hilt 编译期的错误提示才是真正的救命稻草 —— 别急,我们一点点来。