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