@HiltAndroidApp 未正确使用:Application 类未声明与多 Application 场景
说实话,这个错误我见过太多次了。尤其在团队里新人刚接触 Hilt 时,第一件事就是忘了给 Application 加注解。或者更头疼的是——项目中莫名其妙多了两个 Application 类,每个都标了 @HiltAndroidApp,结果编译直接炸了。
咱们今天就把这两个坑彻底排查清楚。我习惯把这类问题归类为“入口配置错误”,因为 Hilt 的整个依赖注入体系,全靠 Application 这个入口来启动。入口不对,后面所有组件全白搭。
1. 症状:编译报错 "Hilt Android Gradle plugin is applied but @HiltAndroidApp not found"
这应该是最常见的提示了。你看一下报错信息:
error: [Hilt] Hilt Android Gradle plugin is applied, but no @HiltAndroidApp annotation was found in your AndroidManifest.xml's Application class.
翻译成人话就是:伙计,你开了 Hilt 开关,但你的 Application 类上没写 @HiltAndroidApp 注解。Hilt 启动不了。
我还记得有一次排查了半小时,最后发现是同事把注解写在了 activity 上,而不是 Application。嗯,这种低级错误谁都犯过。
正确做法:在你的 Application 类上加上 @HiltAndroidApp,然后确保 AndroidManifest.xml 里引用的就是这个类。
@HiltAndroidApp
class MyApplication : Application() { ... }
同时 AndroidManifest.xml 里:
<application
android:name=".MyApplication"
...>
2. 更隐蔽的错误:多个 Application 同时标注 @HiltAndroidApp
这个场景多发生在大型项目里。比如说主工程有一个 Application,某个 library 模块也自己定义了一个 Application,并且两个类上都标了 @HiltAndroidApp。
编译时你会看到类似这样的错误:
error: [Hilt] @HiltAndroidApp can only be applied to one Application class. Found multiple: [com.example.MainApplication, com.example.lib.LibApplication]
其实 Hilt 不允许出现两个入口。为什么呢?因为 Hilt 在编译时只生成一套单例组件树(SingletonComponent),只能由一个 Application 来持有。多个入口会导致组件树冲突。
我曾经在重构时踩过这个坑:一个多模块项目里,主 app 和 push 模块都有自己的 Application。两个都加 @HiltAndroidApp,结果编译直接崩了。后来我把 push 模块里的 Application 去掉,只保留主 app 的入口,然后在 push 模块里通过 Hilt 的入口点(EntryPoint)来获取依赖。
3. 多 Application 场景的三种解决思路
你想想看,如果你确实需要多个 Application(比如主进程、后台推送进程、isolated 进程),该怎么办?这里我总结了三套方案:
| 方案 | 适用场景 | 操作方式 |
|---|---|---|
| 合并 Application | 两个 Application 职责相近,可整合 | 去掉一个 @HiltAndroidApp,把逻辑移到主 Application |
| 仅主进程标记 @HiltAndroidApp | 多进程且只有主进程需要 Hilt | 其他进程 Application 不加注解,手动创建 HiltComponent |
| 使用 @HiltAndroidApp 加组件适配器 | 需要为不同进程提供不同依赖图 | 用 Hilt 的 @EarlyEntryPoint 等机制拆分组件 |
我个人比较推荐方案一,简单粗暴。但如果业务必须拆开,那方案二更干净——只有主 Application 加 @HiltAndroidApp,其他进程 Application 自己通过 Hilt 的 EntryPoints 访问需要的依赖。
4. 实战验证:如何检查你是否正确配置
在你写完代码后,别急着编译。先手动确认三点:
- 搜索整个项目,看看 @HiltAndroidApp 出现了几次。如果超过一次,立刻排查。
- 检查 AndroidManifest.xml 中的
android:name是否指向你标注了注解的那个 Application 类。 - 检查 library 模块:有些 Android Library 也可能包含 Application 类定义,确保它们没有标注 @HiltAndroidApp。
小技巧:用 Android Studio 的全局查找(Ctrl+Shift+F),搜 "@HiltAndroidApp"。如果结果里出现两个以上的文件,基本上就是有问题了。
5. 一个容易忽略的点:Application 的 attachBaseContext 与 Hilt 初始化
有时候你明明加上了 @HiltAndroidApp,编译也通过了,但运行时 Hilt 组件就是没初始化。怎么回事?
八成是你重写了 Application.attachBaseContext() 但没有调用 super.attachBaseContext(),或者调用的时机不对。Hilt 在 attachBaseContext 中完成组件初始化,你必须保证 super 调用在最前面。来看看正确的姿势:
@HiltAndroidApp
class MyApplication : Application() {
override fun attachBaseContext(base: Context) {
super.attachBaseContext(base) // 一定要在开头调用,Hilt 依赖这个
// 你的其他逻辑
}
}
如果顺序写反了,Hilt 组件树可能没来得及创建,导致注入失败。我有个朋友曾因这个 bug 排查了大半天,最后发现是某第三方 SDK 要求必须在 attachBaseContext 里做某些初始化,程序员顺手把 super 调用挪到了后面。
6. 终极检测:clean build 一次
有时候 Hilt 的编译缓存会导致旧配置残留。改了 Application 注解后,最好做一次 Build → Clean Project 再重新编译。我见过有的同事改了代码,但编译缓存没清,一直报错,折腾了 20 分钟才发现。