Hilt初始化时机:Application.onCreate()中的初始化
说白了吧,Hilt的初始化时机藏在 Application.onCreate() 里。我第一次接手一个已经上线的项目时,发现冷启动速度比竞品慢了快1秒。一查,问题就出在Hilt初始化把主线程卡住了。
嗯,这一章我们就来深挖 Hilt到底在Application.onCreate()里做了什么,以及为什么它可能成为启动性能的瓶颈。
一、Hilt初始化的“幕后黑手”
当你给Application类加上 @HiltAndroidApp 注解后,Hilt会生成一个名为 Hilt_YourApplication 的基类。这个基类的 onCreate() 方法里,会触发Hilt的组件初始化流程。
@HiltAndroidApp
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
// 你以为这里空空的?其实Hilt已经偷偷插入了代码
}
}
// Hilt生成的基类(简化后)
public class Hilt_MyApp extends Application {
@Override
public void onCreate() {
inject();
super.onCreate();
}
private void inject() {
// 创建SingletonComponent,扫描所有@InstallIn模块
// 构建对象图,实例化所有单例
}
}
你看,Hilt的初始化就是在这个 inject() 方法里完成的。它会做三件事:
- 创建
SingletonComponent(应用级组件) - 收集所有通过
@Module @InstallIn(SingletonComponent.class)注册的模块 - 实例化所有标记为
@Singleton的依赖(比如OkHttpClient、Retrofit等)
关键点: 这三个步骤全是同步执行的,而且跑在主线程上。
二、性能隐患:初始化耗时到底有多大?
我参与过一个短视频App的项目,当时冷启动时有明显白屏。我们在 onCreate() 前后加了时间戳日志,结果发现Hilt初始化就花了 800ms。为什么这么慢?
- 项目里有十几个模块,每个模块都在
SingletonComponent里绑定了大量提供者方法 - 有几个网络库的初始化(比如连接池预热)特别重
- 第三方SDK的Hilt模块也在Application层初始化
避坑指南: 我曾经在 Application.onCreate() 里直接写了一个 Thread.sleep(500) 来模拟耗时初始化,结果冷启动时系统直接弹了ANR对话框——因为主线程阻塞超过5秒就会被系统杀掉。所以即使是Hilt自带的初始化,只要内容多了,也极有可能造成启动慢甚至ANR。
三、复现延迟问题的标准步骤
如果你也想在本地复现Hilt初始化导致的启动延迟,可以按这个步骤来:
- 第一步:新建一个带Hilt的Android项目,确保Application类加了
@HiltAndroidApp - 第二步:在Application中重写一个“假”的延时初始化模块
@Module @InstallIn(SingletonComponent.class) public class HeavyInitModule { @Provides @Singleton public HeavyObject provideHeavyObject() { // 模拟耗时操作 try { Thread.sleep(500); // 故意延迟500ms } catch (InterruptedException e) { e.printStackTrace(); } return new HeavyObject(); } } - 第三步:在Application.onCreate()里打印启动时间
@HiltAndroidApp public class DemoApp extends Application { @Override public void onCreate() { long start = System.currentTimeMillis(); super.onCreate(); // 触发Hilt初始化 long cost = System.currentTimeMillis() - start; Log.d("HiltInit", "Hilt初始化耗时: " + cost + "ms"); } } - 第四步:运行App,观察日志。你会看到类似
Hilt初始化耗时: 520ms的输出。
小贴士: 如果项目大但没加耗时模块,就用 Trace 工具(Android Studio Profiler)抓一下主线程的调用栈,可以清晰地看到Hilt的 inject() 方法占用了多少时间。
四、核心流程:一张图看懂
下面这个流程图画了Hilt在Application.onCreate()中初始化的完整路径。从左到右,你看到的就是冷启动时主线程的工作顺序——**初始化越重,白屏时间越长**。
五、如何定位初始化中的“罪魁祸首”?
如果你已经在项目里发现了启动慢,别急着改架构。先找出哪个模块最耗时间。我一般用两个方法:
| 方法 | 操作 | 优点 |
|---|---|---|
| 手工日志 | 在每个@Provides方法开头和结尾打印时间戳 |
简单直接,适合少量模块 |
| Android Profiler | 录制Method Trace,查看主线程上Hilt相关方法的执行时长 | 全局视角,不用改代码 |
| AOP/字节码 | 用AspectJ或Transform API插桩Hilt生成的注入方法 | 自动化,适合大型团队 |
我个人的习惯: 在项目初期就用手工日志打一遍所有Singleton模块的初始化耗时,并把它写进CI的启动性能基线。如果新提交的代码导致某个模块初始化超过阈值,就直接发告警。这样能避免后期“积重难返”。
六、避坑总结
- 不要在Application.onCreate()里放重初始化代码——比如图片库、热修复、网络库的初始化,可以考虑移到第一个Activity的延迟加载里
- @Singleton里的对象越少越好——能做成
@ActivityScoped或@FragmentScoped的就别放全局 - 第三方SDK的Hilt模块要慎用——有些SDK的初始化里藏着网络请求或文件IO,一定要评测后再引入