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 三个注解的协作流程

这三个注解是怎么配合工作的?我画了一张图帮你理解:

Hilt 核心注解协作流程图 @HiltAndroidApp Application 类上 编译期生成 Hilt 依赖图 (自动生成 BaseApplication、Hilt_ 等类) @AndroidEntryPoint Activity / Fragment / View / Service @Inject 注入依赖 ⚠️ 常见错误 忘记加 @HiltAndroidApp → 编译报错 Fragment 没加 @AndroidEntryPoint → 运行时崩溃 @Inject 用在 抽象类上 → 编译失败

这张图把流程说得很清楚了:

  1. @HiltAndroidApp 在Application类上开启Hilt功能
  2. 编译期自动生成依赖图的基础代码
  3. @AndroidEntryPoint 让具体组件接入依赖图
  4. @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