3. Hilt核心注解:@HiltAndroidApp、@AndroidEntryPoint、@Inject
好,咱们正式开始撸Hilt的代码。这一章我会把Hilt最核心的三个注解讲透。说白了,你只要搞懂这三个注解,Hilt的架子就能搭起来。我在项目中见过太多人一上来就搞什么自定义组件、Qualifier,结果连最基本的入口注解都写错了位置——嗯,那确实挺让人头疼的。
3.1 @HiltAndroidApp:应用的“总开关”
这个注解必须放在你的Application类上。为什么?因为Hilt需要在应用启动时生成一套完整的依赖图。你可以把它想象成整个依赖注入系统的“总电闸”。
核心要点:@HiltAndroidApp 会触发Hilt的代码生成器,在编译期生成所有依赖的基类。没有它,后面的所有注解都白搭。
@HiltAndroidApp
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// 这里不需要任何Hilt初始化代码
// Hilt会在Application创建时自动完成初始化
}
}
我个人习惯在项目初期就把这个注解写好。你想想看,如果项目都写了一半才发现Application类没加注解,那改起来可就麻烦了——所有依赖注入的地方都得重新编译。
小提示:如果你用的是多Module项目,每个Module的Application类都要加@HiltAndroidApp吗?不需要。整个应用只需要一个Application类加这个注解就够了。子Module通过@AndroidEntryPoint来接入。
3.2 @AndroidEntryPoint:组件的“入场券”
这个注解的作用很直接——告诉Hilt:这个Android组件(Activity、Fragment、View、Service、BroadcastReceiver)需要参与依赖注入。
我记得有一次,同事在Fragment里加了@Inject注解,但忘了在Fragment类上加@AndroidEntryPoint。结果一运行就崩,报错信息说“Hilt无法解析该Fragment的依赖”。嗯,这个坑我踩过,所以特别提醒你。
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
// 这些依赖会在Activity创建时自动注入
@Inject
lateinit var userRepository: UserRepository
@Inject
lateinit var analyticsService: AnalyticsService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 注意:此时userRepository和analyticsService已经可用了
// 不需要手动调用任何注入方法
setContentView(R.layout.activity_main)
}
}
重要提醒:@AndroidEntryPoint 注解的组件,其子组件也必须加注解。比如,一个@AndroidEntryPoint Activity里的Fragment,如果也需要注入,那这个Fragment也必须加上@AndroidEntryPoint。否则Hilt会直接报错。
这里有个表格,帮你快速对照哪些组件支持@AndroidEntryPoint:
| 组件类型 | 是否支持 | 注意事项 |
|---|---|---|
| Activity | ✅ 支持 | 所有Activity都支持,包括AppCompatActivity |
| Fragment | ✅ 支持 | 需要AndroidX Fragment库 |
| View | ✅ 支持 | 自定义View也可以 |
| Service | ✅ 支持 | 包括前台Service |
| BroadcastReceiver | ✅ 支持 | 记得在Manifest中注册 |
| ContentProvider | ❌ 不支持 | Hilt官方未提供支持 |
3.3 @Inject:告诉Hilt“我需要什么”和“怎么创建”
@Inject注解有两个用途,很多人只知其一不知其二。它既可以标注在构造方法上,也可以标注在字段上。
3.3.1 标注在构造方法上:告诉Hilt如何创建对象
// 告诉Hilt:这个类我可以帮你创建
class UserRepository @Inject constructor(
private val apiService: ApiService,
private val database: Database
) {
fun getUser(id: String): User {
// 业务逻辑
}
}
当你在构造方法上加@Inject,Hilt就知道:哦,这个类我可以自动实例化。它的所有参数,Hilt也会尝试去创建。这就是所谓的“依赖链”——Hilt会递归地解析所有依赖。
3.3.2 标注在字段上:告诉Hilt“我需要这个依赖”
@AndroidEntryPoint
class ProfileFragment : Fragment() {
// 字段注入
@Inject
lateinit var userRepository: UserRepository
@Inject
lateinit var imageLoader: ImageLoader
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 直接使用注入的依赖
loadUserProfile()
}
}
关键区别:构造方法注入是Hilt最推荐的方式,因为它能保证依赖在对象创建时就可用。字段注入虽然方便,但依赖是在对象创建后才注入的,所以必须用lateinit var。
3.4 三个注解的协作流程
这三个注解是怎么配合工作的?我画了一张图帮你理解:
这张图把流程说得很清楚了:
- @HiltAndroidApp 在Application类上开启Hilt功能
- 编译期自动生成依赖图的基础代码
- @AndroidEntryPoint 让具体组件接入依赖图
- @Inject 在运行时完成依赖的注入
3.5 避坑指南
我曾经在好几个项目里看到过这些错误,今天一并告诉你:
坑1:@Inject 不能用在接口或抽象类上。Hilt需要知道具体怎么创建对象,接口没有构造方法,所以不行。这种情况需要用@Binds或@Provides,后面章节会讲。
坑2:@AndroidEntryPoint 的组件,其父类如果也有注入需求,父类也必须加注解。比如你写了一个BaseActivity,里面用了@Inject,那BaseActivity也得加@AndroidEntryPoint。
我的建议:刚开始用Hilt时,先在一个简单的Activity里跑通这三个注解的流程。确认没问题了,再扩展到Fragment、Service等其他组件。这样出了问题也好排查。
3.6 小结
这三个注解就是Hilt的“三驾马车”。@HiltAndroidApp负责启动,@AndroidEntryPoint负责接入,@Inject负责具体注入。你只要记住这个顺序,Hilt的基本用法就掌握了八成。
嗯,下一章我们会深入@Module和@Provides,到时候你会看到Hilt更强大的地方。不过在那之前,先把这三个注解练熟——我建议你新建一个项目,从Application到Activity再到Fragment,完整跑一遍注入流程。实践出真知嘛。
公众号:蓝海资料掘金营,微信deep3321