You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

迁移至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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 08:15:30