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

如何将MainActivity的字符串传入单例ServiceBuilder并修复Retrofit动态url失效问题

问题根因
  • Kotlin的object单例的非懒加载属性会在类首次被访问的时候一次性完成初始化,你的retrofit实例在你第一次调用ServiceBuilder的任何方法之前就已经用默认的url = "http://no-google.com"构建完成了
  • 后续调用loadUrl()修改的只是普通变量url的值,已经构建好的Retrofit实例的baseUrl不会自动更新,所以设置不生效
符合开发规范的解决方案

根据业务场景可以选择两种不同的实现方案:

场景1:App存在多个固定的baseUrl,需要动态切换

推荐使用OkHttp拦截器修改请求地址的方案,不需要重复构建Retrofit实例,性能更高:

object ServiceBuilder {
    // 存储动态切换的baseUrl
    var dynamicBaseUrl: String = "http://no-google.com"

    private val baseUrlInterceptor = Interceptor { chain ->
        val originalRequest = chain.request()
        val originalUrl = originalRequest.url

        // 用dynamicBaseUrl替换原来的baseUrl部分
        val newUrl = originalUrl.newBuilder()
            .scheme(URL(dynamicBaseUrl).protocol)
            .host(URL(dynamicBaseUrl).host)
            .port(URL(dynamicBaseUrl).port)
            .build()

        val newRequest = originalRequest.newBuilder()
            .url(newUrl)
            .build()
        chain.proceed(newRequest)
    }

    val headerInterceptor = object: Interceptor {
        override fun intercept(chain: Interceptor.Chain): Response {
            var request = chain.request()
            request = request.newBuilder()
                .addHeader("x-device-type", Build.DEVICE)
                .addHeader("Accept-Language", Locale.getDefault().language)
                .build()
            return chain.proceed(request)
        }
    }

    private var logger = HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)

    // OkHttp客户端全局复用
    private val okHttp = OkHttpClient.Builder()
        .callTimeout(5, TimeUnit.SECONDS)
        .addInterceptor(headerInterceptor)
        .addInterceptor(baseUrlInterceptor)
        .addInterceptor(logger)
        .build()

    // Retrofit只初始化一次,baseUrl后续会被拦截器替换
    private val retrofit = Retrofit.Builder()
        .baseUrl(dynamicBaseUrl)
        .addConverterFactory(GsonConverterFactory.create())
        .client(okHttp)
        .build()

    fun loadUrl(url: String): ServiceBuilder {
        this.dynamicBaseUrl = url
        return this
    }

    fun <T> buildService(serviceType: Class<T>): T {
        return retrofit.create(serviceType)
    }
}

场景2:baseUrl完全不固定,每次调用可能都不一样

这种场景需要每次设置新url之后重新构建Retrofit实例,修改代码如下:

object ServiceBuilder {
    var url = "http://no-google.com"

    fun loadUrl(url: String): ServiceBuilder{
        this.url = url
        return this
    }

    private var logger = HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)

    val headerInterceptor = object: Interceptor {
        override fun intercept(chain: Interceptor.Chain): Response {
            var request = chain.request()
            request = request.newBuilder()
                .addHeader("x-device-type", Build.DEVICE)
                .addHeader("Accept-Language", Locale.getDefault().language)
                .build()
            return chain.proceed(request)
        }
    }

    // OkHttp客户端可以全局复用,不用每次重新构建
    private val okHttp = OkHttpClient.Builder()
        .callTimeout(5, TimeUnit.SECONDS)
        .addInterceptor(headerInterceptor)
        .addInterceptor(logger)
        .build()

    fun <T> buildService(serviceType: Class<T>): T {
        // 每次构建Service的时候用最新的url重新生成Retrofit实例
        val retrofit = Retrofit.Builder()
            .baseUrl(url)
            .addConverterFactory(GsonConverterFactory.create())
            .client(okHttp)
            .build()
        return retrofit.create(serviceType)
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 18:06:04