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

如何在Hilt中使用Dagger的Provider<T>接口?

在Hilt中使用Dagger Provider实现异步单例初始化

要在Hilt中借助Provider<T>实现类的非主线程异步初始化并保证单例,核心思路是利用Provider<T>延迟实例创建时机,再通过后台线程触发初始化并控制单例唯一性,具体步骤如下:

1. 定义目标初始化类

确保你的耗时初始化类具备@Inject构造函数(或在Hilt模块中提供绑定),让Hilt能为其生成Provider<T>:

class HeavySdkClient @Inject constructor() {
    init {
        // 模拟耗时初始化操作(如加载配置、连接服务)
        Thread.sleep(2000)
    }

    fun executeBusinessLogic() {
        // 业务方法
    }
}

2. 注入Provider<T>而非直接注入实例

在负责异步初始化的单例管理器中,注入Provider<HeavySdkClient>,而非直接注入HeavySdkClient——这样Hilt不会在依赖注入阶段提前初始化该类,仅在调用Provider.get()时才触发实例创建:

@Singleton
class SdkInitializer @Inject constructor(
    private val sdkProvider: Provider<HeavySdkClient>
) {
    private var initializedSdk: HeavySdkClient? = null
    private val lock = Any()

    // 异步获取实例,回调返回结果
    fun getSdkAsync(callback: (HeavySdkClient) -> Unit) {
        // 已初始化直接返回
        initializedSdk?.let {
            callback(it)
            return
        }

        // 双重校验锁保证仅初始化一次
        synchronized(lock) {
            initializedSdk?.let {
                callback(it)
                return
            }
            // 后台线程执行初始化
            CoroutineScope(Dispatchers.IO).launch {
                val sdkInstance = sdkProvider.get()
                synchronized(lock) {
                    initializedSdk = sdkInstance
                }
                // 回到主线程回调结果(可选,根据业务需求调整)
                withContext(Dispatchers.Main) {
                    callback(sdkInstance)
                }
            }
        }
    }

    // 或用Flow返回实例(更贴合Kotlin协程生态)
    fun getSdkFlow(): Flow<HeavySdkClient> = flow {
        initializedSdk?.let {
            emit(it)
            return@flow
        }
        synchronized(lock) {
            initializedSdk?.let {
                emit(it)
                return@flow
            }
            val sdkInstance = sdkProvider.get()
            initializedSdk = sdkInstance
            emit(sdkInstance)
        }
    }.flowOn(Dispatchers.IO)
}

3. 在业务代码中使用异步初始化的实例

在ViewModel、Activity等组件中注入SdkInitializer,通过它获取初始化完成的实例:

@HiltViewModel
class MainViewModel @Inject constructor(
    private val sdkInitializer: SdkInitializer
) : ViewModel() {

    fun useSdk() {
        // 回调方式
        sdkInitializer.getSdkAsync { sdk ->
            sdk.executeBusinessLogic()
        }

        // Flow方式(推荐,适配生命周期)
        viewModelScope.launch {
            sdkInitializer.getSdkFlow().collect { sdk ->
                sdk.executeBusinessLogic()
            }
        }
    }
}

关键注意事项

  • 如果目标类本身需要全局单例,可以在Hilt模块中为其添加@Singleton绑定:
    @Module
    @InstallIn(SingletonComponent::class)
    object AppModule {
        @Provides
        @Singleton
        fun provideHeavySdkClient(): HeavySdkClient {
            return HeavySdkClient()
        }
    }
    
    此时Provider.get()会始终返回同一个单例实例,首次调用时的初始化仍在后台线程执行。
  • 避免直接在主线程调用Provider.get(),否则会阻塞UI,必须通过后台线程触发。

内容的提问来源于stack exchange,提问作者me.at.coding

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 23:30:47