Retrofit 動態管理和修改 BaseUrl,從未如此簡單
- 2019 年 10 月 4 日
- 筆記
需求場景
在使用retrfoit做網路請求開發的時候,如果app涉及到多個不同 BaseUrl,僅僅是baseUrl不同,retrofit的其他配置都是一樣的,我們不得不創建管理多個retrofit實例,或者需要在Service介面處修改@Get @Url等傳入完整的url地址。這其實不是我們所期望的,因為如果有很多不同baseurl 地址的請求,我們可能某個baseurl只有一個或者很少的service使用的到,亦或者很多介面需要頻繁的調用。我們不期望管理多個retrofit實例,是否可以只創建一個retrofit對象就能解決全部問題呢?答案肯定是可以的。
一幫的解決方案
- 配置多個retrofit對象
- okhttp攔截器
Interceptor
的intercept(Chain chain)
方法里做攔截
具體實現的話網上很多,這裡不多做介紹
基於okhttps.Call.Factory
的解決方案
研究retrofit的源程式碼我們知道,retrofit最終發起請求是從OkHttpCall
裡面,createRawCall
方法創建最終的okhttp3.Call
對象,下面是程式碼
private okhttp3.Call createRawCall() throws IOException { okhttp3.Call call = callFactory.newCall(requestFactory.create(args)); if (call == null) { throw new NullPointerException("Call.Factory returned null."); } return call; }
我們可以看到是通過callFactory對象創建的,那我們能不能從newCall方法裡面做文章呢。
callFactory
是從哪裡來的呢?可以看一下Retrofit
這個類的程式碼可以知道,是通過client和callFactory
這兩個方法賦值的,那怎麼才能攔截上面的newCall
方法,當然是自定義CallFactory
了。
/** * The HTTP client used for requests. * <p> * This is a convenience method for calling {@link #callFactory}. */ public Builder client(OkHttpClient client) { return callFactory(checkNotNull(client, "client == null")); } /** * Specify a custom call factory for creating {@link Call} instances. * <p> * Note: Calling {@link #client} automatically sets this value. */ public Builder callFactory(okhttp3.Call.Factory factory) { this.callFactory = checkNotNull(factory, "factory == null"); return this; }
自定義CallFactory
難道我們要全部重寫OkHttpClient裡面的方法?這裡當然不需要,我們只需要代理一下newCall方法就OK了,核心程式碼如下
/** * 創建時間:2019-09-04 * 編寫人:chengxin * 功能描述:代理{@link okhttp3.Call.Factory} 攔截{@link #newCall(Request)}方法 */ public abstract class CallFactoryProxy implements Call.Factory { private static final String NAME_BASE_URL = "BaseUrlName"; private final Call.Factory delegate; public CallFactoryProxy(Call.Factory delegate) { this.delegate = delegate; } @Override public Call newCall(Request request) { String baseUrlName = request.header(NAME_BASE_URL); if (baseUrlName != null) { HttpUrl newHttpUrl = getNewUrl(baseUrlName, request); if (newHttpUrl != null) { Request newRequest = request.newBuilder().url(newHttpUrl).build(); return delegate.newCall(newRequest); } else { Log.w(RetrofitFactory.TAG, "getNewUrl() return null when baseUrlName==" + baseUrlName); } } return delegate.newCall(request); } @Nullable protected abstract HttpUrl getNewUrl(String baseUrlName, Request request); }
這裡可以看到 我們抽象了 gewNewUrl
方法,只要繼承CallFactoryProxy
,重寫方法getNewUrl
方法,返回新的HttpUrl對象即可。那我們怎麼才能知道哪個請求需要替換HttpUrl呢?通過@Headers或者@Header靜態或者動態的方式都可以替換
@FormUrlEncoded @Headers("BaseUrlName:baidu")//靜態替換 @POST("user/login") Call<LoginInfo> getLogin(@Field("username") String username, @Field("password") String password); @FormUrlEncoded @POST("user/login") Call<LoginInfo> getLogin(@Header("BaseUrlName")/*動態替換*/ String baseUrlName, @Field("username") String username, @Field("password") String password); 構建Retrofit對象 OkHttpClient client = new OkHttpClient.Builder() .build(); Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://wanandroid.com/") .callFactory(new CallFactoryProxy(client) { @Nullable @Override protected HttpUrl getNewUrl(String baseUrlName, Request request) { if (baseUrlName.equals("baidu")) { String oldUrl = request.url().toString(); String newUrl = oldUrl.replace("https://wanandroid.com/", "https://www.baidu.com/"); return HttpUrl.get(newUrl); } return null; } }) .addConverterFactory(GsonConverterFactory.create()) .build();
這裡對getNewUrl
的處理比較簡單,開發者可以自己實現和管理,這個方法是開放的。
是不是綠色、環保、無污染,程式碼侵入性極低。
源碼地址:
https://github.com/xchengDroid/retrofit-helper