迁移至Hilt后出现lateinit属性app未初始化错误
Dagger迁移Hilt后启动崩溃:lateinit property
app未初始化 我正在将代码库从Dagger 2迁移到Hilt,代码能正常编译,但应用启动时崩溃,抛出错误:lateinit property app has not been initialized。
Logcat日志
2022-07-27 11:31:26.297 E/AndroidRuntime: FATAL EXCEPTION: main Process: com.appname.company, PID: 17599 java.lang.RuntimeException: Unable to create application com.appname.BaseApplication: kotlin.UninitializedPropertyAccessException: lateinit property app has not been initialized at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6767) at android.app.ActivityThread.access$1500(ActivityThread.java:256) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2091) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loopOnce(Looper.java:201) at android.os.Looper.loop(Looper.java:288) at android.app.ActivityThread.main(ActivityThread.java:7870) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003) Caused by: kotlin.UninitializedPropertyAccessException: lateinit property app has not been initialized at com.appname.BaseApplication$Companion.getApp(BaseApplication.kt:746) at com.appname.BaseApplication$Companion$sharedPrefs$2.invoke(BaseApplication.kt:839) at com.appname.BaseApplication$Companion$sharedPrefs$2.invoke(BaseApplication.kt:838) at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74) at com.appname.BaseApplication$Companion.getSharedPrefs(BaseApplication.kt:838) at com.appname.BaseApplication$Companion.getOverrideUrl(BaseApplication.kt:894) at com.appname.BaseApplication$Companion.getBaseUrl(BaseApplication.kt:908) at com.appname.db.network.NetworkService.<init>(NetworkService.kt:47) at com.appname.DaggerBaseApplication_HiltComponents_SingletonC$Builder.build(DaggerBaseApplication_HiltComponents_SingletonC.java:308) at com.appname.Hilt_BaseApplication$1.get(Hilt_BaseApplication.java:22) at dagger.hilt.android.internal.managers.ApplicationComponentManager.generatedComponent(ApplicationComponentManager.java:40) at com.appname.Hilt_BaseApplication.generatedComponent(Hilt_BaseApplication.java:33) at com.appname.Hilt_BaseApplication.onCreate(Hilt_BaseApplication.java:41) at com.appname.BaseApplication.onCreate(BaseApplication.kt:182) at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1223)
相关代码片段
BaseApplication.kt
@HiltAndroidApp open class BaseApplication : Application() { .... @Inject lateinit var hsAnalytics: HsAnalytics .... @Inject @field:Named(NetworkService.Keys.GRAPH_QL_OKHTTP_CLIENT) lateinit var okHttpClient: OkHttpClient @Inject lateinit var apolloClient: ApolloClient .... override fun onCreate() { super.onCreate() app = this .... } companion object { @JvmStatic lateinit var app: BaseApplication private set .... } }
AppModule.kt
@Module @InstallIn(SingletonComponent::class) object AppModule { @Singleton @Provides fun provideApplication(@ApplicationContext application: BaseApplication): BaseApplication { return application as BaseApplication } @Provides @Singleton internal fun provideContext(application: BaseApplication): Context = application }
NetworkService.kt(关键部分)
@InstallIn(SingletonComponent::class) @Module class NetworkService @Inject constructor() { private val baseUrl: String = BaseApplication.baseUrl private val feedBaseUrl: String = BaseApplication.feedBaseUrl // ... 其他代码中还多处引用BaseApplication.sharedPrefs、BaseApplication.appResources等静态属性 }
问题原因
Hilt与Dagger的初始化顺序存在本质差异:
- Dagger中,组件初始化时机由开发者控制,通常在
Application.onCreate()中手动创建组件,此时app静态变量已完成赋值,依赖不会提前访问它。 - 但使用
@HiltAndroidApp注解后,Hilt会生成代理类Hilt_BaseApplication,该类会在**Application.onCreate()执行之前**创建并初始化SingletonComponent组件。
从日志调用链可清晰看到:DaggerBaseApplication_HiltComponents_SingletonC$Builder.build() → NetworkService.<init> → BaseApplication.baseUrl → BaseApplication.sharedPrefs → BaseApplication.app,此时BaseApplication.onCreate()尚未执行,app变量自然处于未初始化状态。
解决方案
1. 移除静态依赖,改用Hilt注入(推荐长期方案)
NetworkService作为Hilt模块,应通过构造函数或参数注入获取所需上下文、配置,而非直接访问静态变量:
修改NetworkService:
@InstallIn(SingletonComponent::class) @Module class NetworkService @Inject constructor( @ApplicationContext private val context: Context, private val sharedPrefs: SharedPreferences ) { // 通过注入的依赖获取baseUrl,替代静态引用 private val baseUrl: String = getBaseUrl(context, sharedPrefs) private val feedBaseUrl: String = getFeedBaseUrl(context, sharedPrefs) // ... 其他方法中,将BaseApplication.sharedPrefs替换为注入的sharedPrefs,BaseApplication.appResources替换为context.resources private fun getBaseUrl(context: Context, sharedPrefs: SharedPreferences): String { val overrideUrl = sharedPrefs.getString(AppConstants.OVERRIDE_URL, null) return overrideUrl ?: context.getString(R.string.default_base_url) } private fun getFeedBaseUrl(context: Context, sharedPrefs: SharedPreferences): String { // 实现对应逻辑 } }
同时在AppModule中提供SharedPreferences实例:
@Module @InstallIn(SingletonComponent::class) object AppModule { // ... 其他provide方法 @Singleton @Provides fun provideSharedPreferences(application: BaseApplication): SharedPreferences { return application.getSharedPreferences(AppConstants.PREF_NAME, Context.MODE_PRIVATE) } }
2. 临时调整静态变量初始化时机(应急方案)
若代码库中大量静态引用无法一次性替换,可在BaseApplication的构造函数中初始化app变量(构造函数会在onCreate之前执行):
@HiltAndroidApp open class BaseApplication : Application() { init { app = this } override fun onCreate() { super.onCreate() // 移除原来的app = this .... } companion object { @JvmStatic lateinit var app: BaseApplication private set .... } }
注意:此方法仅作为临时过渡,长期仍需逐步替换静态引用为依赖注入,避免内存泄漏和初始化顺序问题。
3. 批量替换全局静态引用
排查代码中所有BaseApplication.app、BaseApplication.sharedPrefs等静态调用,统一替换为Hilt注入:
- 普通类:通过
@Inject构造函数注入所需依赖; - Android组件(Activity/Fragment):添加
@AndroidEntryPoint注解后注入依赖。
内容的提问来源于stack exchange,提问作者petestmart
相关产品推荐
相关产品推荐

