如何将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
相关产品推荐
相关产品推荐

