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

Android+Kotlin+Hilt:静态工具类依赖注入方案求解

解决Kotlin工具类依赖注入的最优方案

首先明确核心原则:带依赖的工具类不适合用静态类(object/companion object)实现,因为静态结构天生依赖全局状态,和依赖注入(DI)的实例化、解耦思想冲突,会导致初始化繁琐、测试困难、空指针风险等问题。下面针对你的需求给出两种方案,优先推荐第一种。

方案一:将工具类改为普通类,通过Hilt构造注入(推荐)

把AWUtils改成普通类,通过构造函数注入所需的AppSettings和Context,再用Hilt将其标记为单例(如果需要全局唯一实例),这样所有依赖都由Hilt自动管理,无需手动赋值。

实现代码

// 工具类:构造注入依赖
class AWUtils @Inject constructor(
    private val appSettings: AppSettings,
    @ApplicationContext private val context: Context // 注入全局ApplicationContext
) {
    fun setAmpersand2Yellow(text2Replace: String, target: String): String {
        return text2Replace.replace(
            target, "<span style=\"color:${appSettings.drawerFooterColor};\">$target</span>"
        )
    }

    val isTablet: Boolean
        get() {
            return (context.resources.configuration.screenLayout
                    and Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE
        }
}

// Hilt模块:提供单例的AWUtils实例
@Module
@InstallIn(SingletonComponent::class)
object AWUtilsModule {
    @Provides
    @Singleton
    fun provideAWUtils(
        appSettings: AppSettings,
        @ApplicationContext context: Context
    ): AWUtils {
        return AWUtils(appSettings, context)
    }
}

// 使用工具类的地方:直接注入实例
@AndroidEntryPoint
class OtherClass @Inject constructor(
    private val awUtils: AWUtils
) {
    fun anyFunction() {
        val myStr = "whatever & test"
        val yellowStr = awUtils.setAmpersand2Yellow(myStr, "&")
        val isTablet = awUtils.isTablet
    }
}

方案优势

  • 完全符合DI思想,依赖由Hilt自动初始化和管理,无需手动赋值
  • 单元测试友好:可以轻松mockAppSettings和Context,验证工具类逻辑
  • 无全局可变状态,避免空指针或未初始化的异常
  • 代码耦合度低,工具类的依赖变更只需修改构造函数,调用方无需调整

方案二:保留object类,通过Application初始化(不推荐)

如果坚持要类似静态调用的体验,可以用object类,但仅在Application的onCreate中初始化一次依赖,避免每次调用都手动赋值。

实现代码

// 工具类:object单例,提供初始化方法
object AWUtils {
    private lateinit var appSettings: AppSettings
    private lateinit var context: Context

    // 仅在Application中调用一次初始化
    fun initialize(appSettings: AppSettings, context: Context) {
        this.appSettings = appSettings
        this.context = context
    }

    fun setAmpersand2Yellow(text2Replace: String, target: String): String {
        // 确保已初始化,避免空指针
        check(::appSettings.isInitialized) { "AWUtils未初始化,请先调用initialize方法" }
        return text2Replace.replace(
            target, "<span style=\"color:${appSettings.drawerFooterColor};\">$target</span>"
        )
    }

    val isTablet: Boolean
        get() {
            check(::context.isInitialized) { "AWUtils未初始化,请先调用initialize方法" }
            return (context.resources.configuration.screenLayout
                    and Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE
        }
}

// 在Application中完成初始化
@HiltAndroidApp
class MyApp : Application() {
    @Inject
    lateinit var appSettings: AppSettings

    override fun onCreate() {
        super.onCreate()
        AWUtils.initialize(appSettings, applicationContext)
    }
}

// 使用时直接调用静态方法
@AndroidEntryPoint
class OtherClass {
    fun anyFunction() {
        val myStr = "whatever & test"
        val yellowStr = AWUtils.setAmpersand2Yellow(myStr, "&")
        val isTablet = AWUtils.isTablet
    }
}

方案缺点

  • 存在全局可变状态,初始化时机依赖Application,若提前调用会抛出异常
  • 单元测试需要手动初始化,灵活性远不如方案一
  • 违背DI的解耦思想,工具类和依赖的耦合度较高

为什么不推荐静态类/companion object?

  • object类:Kotlin的object是饿汉式单例,无法通过构造注入依赖,只能手动设置字段,导致初始化时机不可控,容易出现空指针
  • companion object:属于类的静态成员,Hilt无法为其注入依赖,因为DI是针对类的实例而非静态部分设计的,只能通过类的实例传递依赖,失去了静态调用的意义

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 16:06:26