Android Instrumented Test无法启动:Hilt与Startup库协同使用时的测试阻塞问题
我刚解决过完全一样的问题!当你在AndroidX Startup的Initializer实现中使用Hilt的InitializerEntryPoint进行依赖注入时,Instrumented Test会因为初始化顺序死锁陷入无限等待——而禁用InitializationProvider能正常运行,也验证了问题确实出在Startup和Hilt的初始化交互上。
问题根源
测试环境下,AndroidX Startup的InitializationProvider会在应用启动阶段同步执行所有Initializer的初始化逻辑;而你的SomeLibraryInitializer里,先声明了@Inject lateinit var someLibrary,再调用InitializerEntryPoint.resolve(context).inject(this),这会触发Hilt测试组件的初始化。但此时Hilt的测试组件可能还在等待应用上下文完全初始化,反过来又依赖Startup的Initializer完成,最终形成死锁循环。
解决方案1:测试环境单独禁用Startup初始化(快速临时方案)
不用手动修改主Manifest,你可以在测试目录下创建Manifest覆盖配置,只在测试时禁用InitializationProvider:
在src/androidTest/AndroidManifest.xml中添加:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <application> <!-- 移除Startup的初始化Provider,仅在测试环境生效 --> <provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup" tools:node="remove" /> </application> </manifest>
解决方案2:修复Initializer的注入逻辑(推荐,根治问题)
核心是不要让Initializer类自身依赖Hilt注入,而是直接通过InitializerEntryPoint获取所需实例:
修改你的SomeLibraryInitializer实现:
class SomeLibraryInitializer : Initializer<SomeLibrary> { override fun create(context: Context): SomeLibrary { // 直接从EntryPoint获取SomeLibrary实例,避免在Initializer类上执行注入 val entryPoint = InitializerEntryPoint.resolve(context) return entryPoint.getSomeLibrary() } override fun dependencies(): List<Class<out Initializer<*>>> = listOf(WorkManagerInitializer::class.java) }
同时更新对应的InitializerEntryPoint定义,确保它能返回SomeLibrary:
@EntryPoint @InstallIn(SingletonComponent::class) interface InitializerEntryPoint { fun getSomeLibrary(): SomeLibrary }
这种方式打破了死锁循环——Initializer不再等待自身注入完成,而是直接从Hilt已初始化的组件中获取实例。
额外注意事项
- 确保你的测试类使用
@HiltAndroidTest注解,并且测试Runner配置为HiltTestRunner(如果用的是Hilt专属测试Runner) - 如果
SomeLibrary依赖其他Startup初始化的组件(比如示例中的WorkManager),dependencies()方法的声明必须正确,保证依赖的Initializer先完成初始化
内容的提问来源于stack exchange,提问作者Yauheni Matsiusheuski

