1、Hilt概述:什么是Hilt,Hilt与Dagger的关系,Hilt的优势和适用场景

各位好,我是老张,在Android开发这行摸爬滚打了十来年。今天咱们开篇聊聊Hilt——说实话,这玩意儿刚出来那会儿我还挺抗拒的,觉得“又一个依赖注入框架,学了Dagger还得学它?”后来真用上了才发现,嗯,真香。你想想看,一个能让你少写几十行重复代码、少掉几根头发的东西,凭什么不学?

咱们这章先把基础打牢,搞清楚Hilt到底是个啥,它和Dagger怎么“攀亲戚”的,以及你什么场景下该拿出这把“瑞士军刀”。

1.1 什么是Hilt

Hilt,说白了就是一个专为Android打造的依赖注入(DI)框架。我习惯这么理解:它是Dagger的“官方定制版”——由Google亲手包装,把Dagger里那些繁琐的配置和样板代码统统藏到了幕后。你只需要在类上贴几个注解(比如@HiltAndroidApp、@AndroidEntryPoint),Hilt就会在编译期自动生成一堆Dagger组件和模块,帮你把对象创建、生命周期管理、作用域控制这些破事儿全搞定。

举个例子,以前用Dagger你得自己写@Component、@Subcomponent、@Module,还得手动搭一个AppComponent树。用Hilt呢?三步走:

  • Application类上标个@HiltAndroidApp
  • Activity/Fragment上标个@AndroidEntryPoint
  • 需要注入的字段上标个@Inject

完事了。你是不是觉得跟Retrofit用起来差不多简单?对,Google就是要这个效果。

一个小提示:Hilt要求你Application类必须被@HiltAndroidApp注解,否则所有Hilt功能全趴窝。我记得第一次用Hilt时忘了这步,结果编译报错“No injectable members”,排查了半天……别学我。

1.2 Hilt与Dagger的关系

很多人把Hilt和Dagger对立起来看,其实不对。我这么跟你说吧——Hilt = Dagger + Android专属插件。本质上Hilt还是基于Dagger的@Component、@Module、@Provides那套机制,只是在它上面又加了一层“自动装配”的框架。

来看这张对比表格,一目了然:

维度 Dagger 2 Hilt
组件创建 手动编写 @Component 接口,手动指定模块 自动生成标准组件(SingletonComponent、ActivityComponent等)
作用域绑定 需要手动管理生命周期和子组件 通过 @HiltAndroidApp / @AndroidEntryPoint 自动绑定Android生命周期
模块组织 所有模块手动声明到Component上 用 @InstallIn 指定模块安装到哪个组件,自动化
测试支持 需手动编写测试组件,较麻烦 提供 @HiltAndroidTest 等注解,测试配置大幅简化
学习曲线 陡峭,尤其对新手不友好 平缓很多,基本看一遍官方文档就能上手

为什么Google不让你直接在Dagger上改呢?我当年在项目里也干过类似的事——在Dagger上加了一层自动注册的壳,结果各种编译期循环依赖,欲哭无泪。Google官方团队肯定也踩过这些坑,索性直接搞了个“标准化DI方案”推给你。你想想看,你项目里每个Activity组件该怎么拆、生命周期该怎么绑,Hilt已经帮你定好了“最佳实践”。

下面这张图可以帮你理清Hilt在Dagger生态中的位置:

Hilt在Dagger生态中的位置与分层 Dagger 2 核心层 @Component、@Module、@Provides、@Binds 编译期注解处理、依赖图构建、对象工厂生成 所有DI框架的基础——Hilt的“引擎” Hilt 注解与自动装配层 @HiltAndroidApp @AndroidEntryPoint @HiltViewModel @InstallIn @Module @Provides 等“人机接口” 编译期自动生成 Android 组件、绑定生命周期 Android 集成层 Application / Activity / Fragment / ViewModel / Service ➤ 底层引擎不可替换,上层注解让开发者专注于业务,而非DI配置

1.3 Hilt的优势

我用Hilt做了好几个中型项目之后,总结出它最让我心动的几个点:

  1. 减少样板代码:以前一个Activity要用Dagger注入,你得写Component、Subcomponent、Module、Scope注解……少说五六行配置。Hilt把这一切压到一个@AndroidEntryPoint里。我算过,一个中等规模App能省掉至少300-500行手动编写的DI配置代码。
  2. 标准化组件生命周期:Hilt预定义了SingletonComponent、ActivityComponent、FragmentComponent、ViewModelComponent等,你不用再纠结“这个单例对象到底该绑在哪个作用域”这种问题了。
  3. 编译期安全性:所有依赖分析都在编译期完成,如果有个依赖链出问题了,编译直接报红,不会拖到运行时才崩溃。我有个同事曾用反射手写DI,运行时偶尔挂掉,查了两天才发现是某个字段没初始化——换成Hilt,编译器直接告诉你。
  4. 测试友好:Hilt提供@HiltAndroidTest,配合JUnit4/Mockito,写单元测试和集成测试简直不要太爽。你可以轻松替换测试模块,再也不用写一堆测试Component了。
  5. 与Jetpack无缝集成:比如@HiltViewModel可以直接注入ViewModel,不用手动写ViewModelFactory。我项目里原来自己写一个BaseViewModelFactory,每个ViewModel都要加一段重复代码,用了Hilt之后全部清掉了。

一句话总结优势:Hilt = Dagger的底层威力 + 零样板代码的便利 + Android生命周期的智能绑定。

1.4 适用场景

你可能会问:“老张,我这项目是不是非要用Hilt?” 我给你列几个场景,你自己对号入座:

场景 推荐使用Hilt吗? 原因
新启动的项目,预计有6个以上Activity ✅ 非常推荐 从0搭Hilt,成本最低,后期扩展方便
现有项目使用Dagger,且DI代码混乱 ✅ 推荐迁移 Hilt标准化能清理大量手动组件,但迁移需逐步进行
小型工具类App(只有1~2个Activity) ❌ 没必要 DI框架本身会增加编译时间,小项目手动new反而更快
已经深度定制了一些Dagger子组件 ⚠️ 谨慎 Hilt固定了组件层级,定制化可能受限,建议先局部试用
非常注重编译速度的项目 ❌ 不建议 Hilt的注解处理器会显著增加编译时间(尤其增量编译)

我个人经验是——只要你项目用到超过3个Activity,或者任何一个ViewModel需要注入超过2个依赖,果断上Hilt。 别为那点编译时间纠结,后期维护节省的时间翻倍。

注意:Hilt的编译延迟是真实存在的。尤其是在大型模块化项目中,每个@AndroidEntryPoint都会触发注解处理。我在一个40+模块的项目里,第一次全量编译要5分钟,后来通过Gradle缓存优化才压到2分钟。如果你项目有性能焦虑,可以配合本章后面的“延迟瀑布图排查方法”来定位哪个模块拖累了编译。

1.5 快速上手:第一个Hilt配置

咱们不纸上谈兵,直接看一个最精简的例子。假设你要给一个MainActivity注入一个UserRepository:

// 步骤1:Application类
@HiltAndroidApp
class MyApplication : Application()

// 步骤2:定义可注入的类
class UserRepository @Inject constructor() {
    fun getUser(): String = "Hello from Hilt"
}

// 步骤3:标记Activity
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    @Inject lateinit var userRepository: UserRepository

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.d("Hilt", userRepository.getUser())
    }
}

看到了吗?只用了三个注解,没有@Module、没有@Component、没有@Singleton。如果UserRepository本身没有构造参数,Hilt会自动当作无作用域的单例——每次注入都创建新对象。想让它全局单例?加个@Singleton就行:

@Singleton
class UserRepository @Inject constructor() { ... }

是不是很简单?但千万别觉得Hilt这么简单就完了——它真正的威力在模块化、多绑定、限定符、ViewModel注入这些高级用法上。咱们后面的章节会逐个击破。