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

Hilt如何重新初始化Application作用域Retrofit实现BaseUrl切换

Hilt 场景下动态切换 Retrofit BaseUrl 实现方案

两种方案均能满足需求,可根据业务场景选择:

方案一:运行时动态替换URL(无需重建实例,优先推荐)

该方案不需要调整现有Retrofit的Application作用域配置,改造成本最低,性能最好。Retrofit本身没有开放修改baseUrl的公开API,但可以通过OkHttp拦截器在请求发起前动态替换目标地址,完全不受初始化时传入的baseUrl限制。
实现步骤:

  • 现有Retrofit初始化逻辑不需要大改,baseUrl()方法随便传入一个合法的默认地址即可,这个值后续会被拦截器覆盖,不会影响实际请求。
  • 自定义OkHttp拦截器,每次请求前从SharedPreferences读取用户当前选中的BaseUrl,替换请求的地址信息:
class DynamicBaseUrlInterceptor @Inject constructor(
    private val sp: SharedPreferences // 直接通过Hilt注入你项目中已封装好的SP实例
) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        // 读取SP中存储的用户选中地址,没有存储值时返回默认环境地址
        val currentBase = sp.getString("KEY_SELECTED_BASE_URL", "https://api.default.com/")!!
        val originalRequest = chain.request()
        val parsedBase = Uri.parse(currentBase)
        // 替换原请求的协议、域名、端口,保留原请求的path、query、header等信息
        val newUrl = originalRequest.url.newBuilder()
            .scheme(parsedBase.scheme)
            .host(parsedBase.host)
            .port(parsedBase.port)
            .build()
        val newRequest = originalRequest.newBuilder().url(newUrl).build()
        return chain.proceed(newRequest)
    }
}
  • 将该拦截器添加到OkHttpClient的拦截器列表中,尽量放在靠前位置,避免其他拦截器拿到旧地址做逻辑处理。
  • 注意事项:所有接口方法的注解中必须使用相对路径(比如@GET("user/info")),不要写全路径地址,否则拦截器不会覆盖对应请求的url。用户切换地址后不需要做其他操作,下一次发起请求时会自动读取最新的SP配置。

方案二:动态重建Retrofit实例(适合多环境配置完全隔离的场景)

如果不同BaseUrl对应完全不同的网络配置(比如不同的超时规则、证书校验逻辑、专属拦截器),可以通过持有可替换的Retrofit实例实现切换,同时支持SP持久化:
实现步骤:

  • 不要直接在Hilt中提供Application作用域的Retrofit实例,改为提供单例的Retrofit管理类,统一负责实例的创建和更新:
@Singleton
class RetrofitManager @Inject constructor(
    private val client: OkHttpClient,
    private val sp: SharedPreferences,
    private val gson: Gson
) {
    @Volatile
    private var currentRetrofit: Retrofit? = null

    // 全局获取Retrofit实例统一走这个方法
    fun get(): Retrofit {
        return currentRetrofit ?: synchronized(this) {
            // 初始化时优先读取SP中存储的用户选中地址
            val savedBase = sp.getString("KEY_SELECTED_BASE_URL", "https://api.default.com/")!!
            buildInstance(savedBase).also { currentRetrofit = it }
        }
    }

    // 用户切换地址时调用这个方法触发实例重建
    fun switchBaseUrl(newBaseUrl: String) {
        // 先持久化到SP,保证冷启动时能读取到最新配置
        sp.edit().putString("KEY_SELECTED_BASE_URL", newBaseUrl).apply()
        synchronized(this) {
            currentRetrofit = buildInstance(newBaseUrl)
        }
    }

    private fun buildInstance(baseUrl: String): Retrofit {
        return Retrofit.Builder()
            .baseUrl(baseUrl)
            .client(client)
            .addConverterFactory(GsonConverterFactory.create(gson))
            // 此处照抄原有Retrofit初始化的其他配置,比如CallAdapter、自定义转换器等
            .build()
    }
}
  • 项目中所有需要创建Api Service的地方,都通过retrofitManager.get().create(xxxApi::class.java)获取实例,不要直接注入Retrofit。
  • 用户在设置页切换BaseUrl选项时,直接调用retrofitManager.switchBaseUrl(选中的目标地址)即可,切换后新发起的请求都会走新创建的Retrofit实例,切换前已经在执行的请求会继续使用旧实例跑完,不会出现请求中断的问题,旧实例无引用后会被GC自动回收,不需要手动处理内存释放。

选型建议:如果只是单纯切换接口域名,没有额外的网络配置差异,直接选方案一即可,改造成本极低,没有任何兼容问题;只有当不同域名对应的网络栈逻辑完全独立时,再考虑方案二。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 19:18:26