前言
多數(shù)項目中會出現(xiàn)用到多個BaseUrl的情況低剔,而Retrofit未提供實時切換BaseUrl的方法当窗,且我們在使用Retrofit以及OkHttp時,通常使用單例模式創(chuàng)建Retrofit和OkHttpClient喷斋,也不可能每個請求都創(chuàng)建一個Retrofit實例庸推。面對這個實際問題,我們需要做的不僅僅是解決當(dāng)前問題洋机,更應(yīng)該從根本上解決該問題坠宴,即實現(xiàn)任何情況下的自動切換BaseUrl。 主要用于個人積累及分享绷旗,如有錯誤請隨時指出喜鼓,文中可能引用其他大牛文章(僅引用鏈接不轉(zhuǎn)載),如有侵權(quán)請告知必妥善處理衔肢。
思路分析
問題
一個例子颠通,給出資源如下:
`//賬戶服地址`
`String base_url_user =` `"[https://www.111.com/](https://www.111.com/)"`
`//支付服地址`
`String base_url_pay =` `"[https://www.222.com/](https://www.222.com/)"`
`//**********賬戶接口方法(使用賬戶服地址base_url_user)`
`String method_path_user_01 =` `"user/login"``;`
`String method_path_user_02 =` `"user/register"``;`
`//**********支付接口方法(使用支付服地址base_url_pay)`
`String method_path_pay_01 =` `"pay/getorder"``;`
`String method_path_pay_02 =` `"pay/payment"``;`
通常情況下,RetrofitService.java中膀懈,我們這樣寫:
@POST("user/login")
Observable<jsonobject> login(@QueryMap
Map<string, object=""> paramMap);
@POST("user/register")
Observable<jsonobject> register(@QueryMap
Map<string, object=""> paramMap);
@POST("pay/getorder")
Observable<jsonobject> getOrder(@QueryMap
Map<string, object=""> paramMap);
@POST("pay/payment")
Observable<jsonobject> payment(@QueryMap
Map<string, object=""> paramMap);
RetrofitHelper.java中這樣定義:
package com.mooc.ppjoke.ui.publish;
public class RetrofitHelper {
private static final String BASE_URL_USER = "https://www.111.com/";
private static final String BASE_URL_PAY = "https://www.222.com/";
private static final long TIME_OUT = 5000;
private RetrofitService retrofitService;
public static RetrofitHelper getInstance() {
return SingleHolder.INSTANCE;
}
private static class SingleHolder {
private static final RetrofitHelper INSTANCE = new
RetrofitHelper();
}
private RetrofitHelper() {
OkHttpClient okHttpClient = new
OkHttpClient
.Builder()
.connectTimeout(TIME_OUT, TimeUnit.MILLISECONDS)
.build();
Retrofit retrofit = new
Retrofit.Builder()
.client(okHttpClient)
.baseUrl(BASE_URL_USER)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
retrofitService = retrofit.create(RetrofitService.class);
}
......
}
如上顿锰,當(dāng)有多個BaseUrl時,Retrofit并沒有提供切換BaseUrl的方法启搂。若是每一個請求均build一個Retrofit硼控,那么單例也就沒什么意義了;若通過在RetrofitService.java的@POST(…)注解中寫死整個url的方式胳赌,也缺乏靈活性牢撼,一旦項目升級再升級,接口數(shù)增多疑苫,或接口有變化時熏版,會特別混亂。
思路
要實現(xiàn)靈活的配置BaseUrl捍掺,首先需要了解OkHttpClient的應(yīng)用攔截器Interceptor撼短。應(yīng)用攔截器Interceptor是當(dāng)前app和okhttp之間的中間層,主要用于截獲app向okhttp的request請求挺勿,并可在回調(diào)中進(jìn)行自定義修改曲横、重置等(另外還有網(wǎng)絡(luò)攔截器NetworkInterceptor,相當(dāng)于okhttp和network之間的中間層,用于截獲okhttp和network之間的請求和響應(yīng))禾嫉。Interceptor通過如下方式添加
OkHttpClient okHttpClient = new
OkHttpClient.Builder()
......
.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
//獲取請求request
Request request = chain.request();
//獲取請求request的創(chuàng)建者builder實例
Request.Builder builder = request.newBuilder();
//通過builder.xxxx.build()修改相關(guān)配置等灾杰,并調(diào)用
//chain.proceed(Request request)返回響應(yīng)response
return
chain.proceed(builder.xxxx.build());
}
})
.build();
上面代碼給我們提供了一個機(jī)會,在okhttp向network請求之前熙参,我們可以很方便的替換請求的內(nèi)容艳吠,只需要再修改builder后,重新build就會生成新的request實例孽椰。
現(xiàn)在已經(jīng)知道了如何在請求network之前修改請求讲竿,那么,剩下需要考慮的問題就是弄屡,我們?nèi)绾闻袛喈?dāng)前的request需要哪一個BaseUrl?
這里我們可以考慮使用@Headers({“xxx_key:yyy_value”})鞋诗,當(dāng)在RetrofitService.java的接口中配置了這個標(biāo)簽膀捷,我們就可以在Interceptor中獲取當(dāng)前請求的頭,并根據(jù)你配置的維一對應(yīng)的yyy_value削彬,來匹配當(dāng)前所需使用的BaseUrl全庸,然后將原有的url替換掉,當(dāng)okhttp向network請求時融痛,就是最新的url了壶笼。
解決
以下將做簡單示例,你可以在項目中根據(jù)實際情況做優(yōu)化雁刷。還是上面的例子覆劈,我們?nèi)缦绿幚砑纯桑?br> RetrofitService.java中
//添加Headers:使用同樣的鍵url_name;使用不同值user或pay沛励,對應(yīng)賬戶服地址和支付服地址责语。下同。
@Headers({"url_name:user"})
@POST("user/login")
Observable<jsonobject> login(@QueryMap
Map<string, object=""> paramMap);
@Headers({"url_name:user"})
@POST("user/register")
Observable<jsonobject> register(@QueryMap
Map<string, object=""> paramMap);
@Headers({"url_name:pay"})
@POST("pay/getorder")
Observable<jsonobject> getOrder(@QueryMap
Map<string, object=""> paramMap);
@Headers({"url_name:pay"})
@POST("pay/payment")
Observable<jsonobject> payment(@QueryMap
Map<string, object=""> paramMap);
RetrofitHelper.java中:
public
class RetrofitHelper {
private static final String BASE_URL_USER = "https://www.111.com/";
private static final String BASE_URL_PAY = "https://www.222.com/";
private static final long TIME_OUT = 5000;
private RetrofitService retrofitService;
public
static RetrofitHelper getInstance() {
return SingleHolder.INSTANCE;
}
private
static class SingleHolder {
private
static final RetrofitHelper INSTANCE = new
RetrofitHelper();
}
private RetrofitHelper() {
OkHttpClient okHttpClient = new
OkHttpClient.Builder()
.connectTimeout(TIME_OUT, TimeUnit.MILLISECONDS)
//添加應(yīng)用攔截器
.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
//獲取request
Request request = chain.request();
//獲取request的創(chuàng)建者builder
Request.Builder builder = request.newBuilder();
//從request中獲取headers目派,通過給定的鍵url_name
List<string> headerValues = request.headers("url_name");
if
(headerValues != null
&& headerValues.size() > 0) {
//如果有這個header坤候,先將配置的header刪除,因此header僅用作app和okhttp之間使用
builder.removeHeader(HttpConfig.HEADER_KEY);
//匹配獲得新的BaseUrl
String headerValue = headerValues.get(0);
HttpUrl newBaseUrl = null;
if
("user".equals(headerValue)) {
newBaseUrl = HttpUrl.parse(BASE_URL_USER);
} else if ("pay".equals(headerValue)) {
newBaseUrl = HttpUrl.parse(BASE_URL_PAY);
} else {
newBaseUrl = oldHttpUrl;
}
//從request中獲取原有的HttpUrl實例oldHttpUrl
HttpUrl oldHttpUrl = request.url();
//重建新的HttpUrl企蹭,修改需要修改的url部分
HttpUrl newFullUrl = oldHttpUrl
.newBuilder()
.scheme(newBaseUrl.scheme())
.host(newBaseUrl.host())
.port(newBaseUrl.port())
.build();
//重建這個request白筹,通過builder.url(newFullUrl).build();
//然后返回一個response至此結(jié)束修改
return
chain.proceed(builder.url(newFullUrl).build());
} else {
return
chain.proceed(request);
}
}
})
.build();
Retrofit retrofit = new
Retrofit
.Builder()
.client(okHttpClient)
//創(chuàng)建retrofit時的baseUrl可以不需擔(dān)心谅摄、隨意指定了
.baseUrl(BASE_URL_USER)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
retrofitService = retrofit.create(RetrofitService.class);
}
......
}