@HiltAndroidApp 未正确使用:Application 类未声明与多 Application 场景

说实话,这个错误我见过太多次了。尤其在团队里新人刚接触 Hilt 时,第一件事就是忘了给 Application 加注解。或者更头疼的是——项目中莫名其妙多了两个 Application 类,每个都标了 @HiltAndroidApp,结果编译直接炸了。

咱们今天就把这两个坑彻底排查清楚。我习惯把这类问题归类为“入口配置错误”,因为 Hilt 的整个依赖注入体系,全靠 Application 这个入口来启动。入口不对,后面所有组件全白搭。

@HiltAndroidApp 错误场景与正确配置流程 ❌ 错误场景 Application 未声明 @HiltAndroidApp Hilt 组件树初始化失败 编译错误:Missing @HiltAndroidApp ❌ 多 Application 场景 类 A 声明 @HiltAndroidApp 类 B 也声明 @HiltAndroidApp 编译冲突:多个 @HiltAndroidApp ✅ 正确配置 唯一 Application 声明 @HiltAndroidApp Hilt 组件树正常初始化 编译通过,DI 正常生效 ⚠️ 核心要点 • Hilt 要求整个应用</tspan><tspan fill="#c0392b" font-weight="bold">只有唯一一个</tspan><tspan> @HiltAndroidApp 注解的 Application 类。 • 如果使用多 Application(如多进程、主客工程),</tspan><tspan fill="#c0392b" font-weight="bold">不能</tspan><tspan>在多个类上标注 @HiltAndroidApp。 • 解决方案:合并 Application,或使用 @HiltAndroidApp 标记主进程 Application,其他进程自行创建 Hilt 组件。 🤔 小提示:如果你必须使用多 Application,可以只让主 Application 加注解,其余进程手动通过 Hilt 入口初始化。

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. 实战验证:如何检查你是否正确配置

在你写完代码后,别急着编译。先手动确认三点:

  1. 搜索整个项目,看看 @HiltAndroidApp 出现了几次。如果超过一次,立刻排查。
  2. 检查 AndroidManifest.xml 中的 android:name 是否指向你标注了注解的那个 Application 类。
  3. 检查 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 分钟才发现。