依赖注入与可变依赖:如何刷新依赖树及处理可变依赖?
这问题太典型了——很多人用依赖注入时都会踩这个「单例依赖可变配置」的坑,我给你拆解几个可行的方案,再说说通用原则:
一、先解决你的当前问题:不用重启也能刷新认证密钥
方案1:让Retrofit拦截器实时获取最新密钥(最优解)
根本不需要重建Repository或Retrofit,只要改一下认证拦截器的逻辑,让它每次请求时动态读取最新的密钥,而不是在Retrofit初始化时就把密钥固定死。
比如用Kotlin写的拦截器示例:
class AuthInterceptor(private val sharedPrefs: SharedPreferences) : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { // 每次请求都从SharedPreferences拿最新的密钥 val latestToken = sharedPrefs.getString("auth_key", null) val request = chain.request().newBuilder() .apply { latestToken?.let { addHeader("Authorization", "Bearer $it") } } .build() return chain.proceed(request) } }
这样不管你什么时候登录刷新密钥,下一次Repository发起请求时,拦截器都会自动用新的密钥,完全不需要重启应用,Repository依然可以保持应用级单例,没有任何额外开销。
方案2:实现可重置的单例容器(适合需要重建Retrofit的场景)
如果你的场景必须重建Retrofit(比如除了密钥还有其他可变配置),可以手动封装一个单例容器,给它加个invalidate方法来清空旧实例,下次使用时自动重建:
object RetrofitContainer { private var retrofitInstance: Retrofit? = null fun getRetrofit(sharedPrefs: SharedPreferences): Retrofit { return retrofitInstance ?: buildNewRetrofit(sharedPrefs).also { retrofitInstance = it } } // 登录成功后调用这个方法清空旧实例 fun invalidate() { retrofitInstance = null } private fun buildNewRetrofit(sharedPrefs: SharedPreferences): Retrofit { return Retrofit.Builder() .baseUrl("https://your-api-domain.com/") .addInterceptor(AuthInterceptor(sharedPrefs)) // 其他配置(比如Gson转换器、OkHttpClient) .build() } }
然后让Repository依赖这个容器的getRetrofit方法,登录刷新密钥后调用RetrofitContainer.invalidate(),下一次Repository发起请求时就会拿到全新的Retrofit实例,用新的密钥。
二、通用解决方案:处理「单例依赖可变元素」的正确姿势
当你需要单例,但它的某个依赖本质可变时,记住这几个核心原则:
- 不要注入静态值,注入「值的提供者」:比如不要把初始化时的密钥字符串传给单例,而是传入SharedPreferences、DataStore这类能实时获取最新值的数据源,或者一个
() -> String类型的函数引用,让单例每次需要时主动获取最新值。 - 让单例保持无状态:把可变状态从单例内部剥离,放到外部的状态管理组件(比如SharedPreferences、ViewModel、DataStore)里,单例只负责业务逻辑,不持有可变状态。
- 避免为了刷新依赖降级作用域:改成Activity级作用域会导致多个实例,反而可能带来状态不一致的问题(比如多个Repository持有不同的密钥),完全是舍本逐末的做法。
内容的提问来源于stack exchange,提问作者Lukasz
相关产品推荐
相关产品推荐

