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

使用Hilt实现私有构造单例类的依赖注入适配问题

问题根因

两个核心问题直接触发了你遇到的编译报错和适配失败:

  • 你给companion object里的getInstance方法加@Inject注解是完全无效的:Kotlin的companion object编译后会生成独立的静态内部类,Dagger遇到这个注解会尝试对Companion类本身做注入,直接触发“不支持注入Kotlin对象”的编译错误。
  • Hilt模块的作用域选错:你把模块绑定到ActivityComponent,但数据层依赖需要全局单例,绑定到Activity组件会导致Activity重建时重复创建依赖实例,和你要的单例语义冲突。
适配方案(无需修改构造函数可见性)

全程保持RemoteDataSource、Repository的私有构造不变,按以下步骤调整即可解决问题:

  • 移除所有加在companion object方法上的@Inject注解,消除Dagger扫描Companion类的触发源
  • 将Hilt模块的安装位置从ActivityComponent调整为全局的SingletonComponent,匹配单例的生命周期
  • 保留类内部的私有构造和静态实例获取方法,通过Hilt的@Provides方法调用这个公开的静态获取方法完成依赖托管
修正后的代码示例

调整后的RemoteDataSource.kt

@Singleton
class RemoteDataSource private constructor(private val jsonHelper: JsonHelper) {

    companion object {
        @Volatile
        private var instance: RemoteDataSource? = null

        // 移除原有的@Inject注解
        fun getInstance(jsonHelper: JsonHelper): RemoteDataSource =
            instance ?: synchronized(this) {
                instance ?: RemoteDataSource(jsonHelper).apply { instance = this }
            }
    }

    fun getAllRemoteMovies(moviesCallback: LoadMoviesCallback) {
        moviesCallback.onAllMoviesReceived(jsonHelper.loadRemoteMovies())
    }

    fun getAllRemoteTVShows(tvshowCallback: LoadTVShowCallback) {
        tvshowCallback.onAllTVShowsReceived(jsonHelper.loadRemoteTVShows())
    }

    interface LoadMoviesCallback {
        fun onAllMoviesReceived(moviesResponses: ArrayList<MovieItem>)
    }

    interface LoadTVShowCallback {
        fun onAllTVShowsReceived(tvshowResponses: ArrayList<TVShowItem>)
    }
}

调整后的Repository.kt

@Singleton
class Repository private constructor(private val remoteDataSource: RemoteDataSource) : DataSource {

    companion object {
        @Volatile
        private var instance: Repository? = null

        // 移除原有的@Inject注解
        fun getInstance(remoteDataSource: RemoteDataSource): Repository =
            instance ?: synchronized(this) {
                instance ?: Repository(remoteDataSource).apply { instance = this }
            }
    }

    override fun getAllRemoteMovies(): LiveData<ArrayList<MovieItem>> {
        val remoteMoviesResult = MutableLiveData<ArrayList<MovieItem>>()
        remoteDataSource.getAllRemoteMovies(object : RemoteDataSource.LoadMoviesCallback {
            override fun onAllMoviesReceived(moviesResponses: ArrayList<MovieItem>) {
                remoteMoviesResult.value = moviesResponses
            }
        })
        return remoteMoviesResult
    }

    override fun getAllRemoteTVShows(): LiveData<ArrayList<TVShowItem>> {
        val remoteTVShowsResult = MutableLiveData<ArrayList<TVShowItem>>()
        remoteDataSource.getAllRemoteTVShows(object : RemoteDataSource.LoadTVShowCallback {
            override fun onAllTVShowsReceived(tvshowResponses: ArrayList<TVShowItem>) {
                remoteTVShowsResult.value = tvshowResponses
            }
        })
        return remoteTVShowsResult
    }
}

调整后的Hilt模块

@Module
// 从ActivityComponent改为SingletonComponent,匹配全局单例生命周期
@InstallIn(SingletonComponent::class)
object RemoteDataSourceModule {

    @Singleton
    @Provides
    // 给Context加上@ApplicationContext注解,避免内存泄漏
    fun provideJsonHelper(@ApplicationContext context: Context): JsonHelper {
        return JsonHelper(context)
    }

    @Singleton
    @Provides
    fun provideRemoteDataSource(jsonHelper: JsonHelper): RemoteDataSource {
        // 直接调用类暴露的静态获取方法,全程不触碰私有构造函数
        return RemoteDataSource.getInstance(jsonHelper)
    }

    @Singleton
    @Provides
    fun provideRepository(remoteDataSource: RemoteDataSource): Repository {
        // 直接调用类暴露的静态获取方法,全程不触碰私有构造函数
        return Repository.getInstance(remoteDataSource)
    }
}
适配说明
  • 调整后因为Hilt的@Singleton作用域绑定在全局SingletonComponent上,整个应用生命周期内只会调用一次对应的@Provides方法,类内部的双重检查锁逻辑会被正确触发,全局仅存在一个类实例,和你原本的单例逻辑完全一致。
  • 整个过程没有将私有构造函数修改为公开,完全满足你的约束要求。
  • 后续给ViewModel注入Repository时,只要在ViewModel的构造函数里加@Inject,Hilt就会自动从依赖图里拿到对应的单例实例,不需要额外处理。

内容的提问来源于stack exchange,提问作者rijamyazid

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.22 09:06:07