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 的容器分层结构画清楚了:

Hilt 组件层级结构图 Hilt 容器层级结构 @HiltAndroidApp (Application) 生命周期 = 应用进程 @AndroidEntryPoint (Activity) 生命周期 = Activity 创建到销毁 @AndroidEntryPoint (Fragment) @HiltViewModel (ViewModel) @AndroidEntryPoint (Service) 关键说明 • 每个容器可以访问其父容器中的依赖(如 Activity 可访问 Application 的依赖) • 子容器销毁时,其作用域内的对象自动释放 • 同一个 Activity 的不同 Fragment 共享该 Activity 的容器

我的建议:刚开始不要纠结所有注解。先记住 @HiltAndroidApp 和 @AndroidEntryPoint 这两个入口注解,再配合 @Inject 声明依赖,就能跑起来。我第一个 Hilt 项目就是这么干的,代码清爽多了。

快速入门:三步走

  1. 加依赖:在 build.gradle (project) 里添加 Hilt 插件,在模块 build.gradle 里引用 hilt-android 和 hilt-compiler。
  2. 打入口:在 Application 类上加 @HiltAndroidApp,在 Activity 上加 @AndroidEntryPoint。
  3. 写注入:在 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 编译期的错误提示才是真正的救命稻草 —— 别急,我们一点点来。