Retrofit 動態管理和修改 BaseUrl,從未如此簡單

需求場景

在使用retrfoit作網絡請求開發的時候,若是app涉及到多個不一樣 BaseUrl,僅僅是baseUrl不一樣,retrofit的其餘配置都是同樣的,咱們不得不建立管理多個retrofit實例,或者須要在Service接口處修改@Get @Url等傳入完整的url地址。這其實不是咱們所指望的,由於若是有不少不一樣baseurl 地址的請求,咱們可能某個baseurl只有一個或者不多的service使用的到,亦或者不少接口須要頻繁的調用。咱們不指望管理多個retrofit實例,是否能夠只建立一個retrofit對象就能解決所有問題呢?答案確定是能夠的。java

一幫的解決方案

  • 配置多個retrofit對象android

  • okhttp攔截器Interceptorintercept(Chain chain)方法裏作攔截git

具體實現的話網上不少,這裏很少作介紹github

基於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方法裏面作文章呢。app

callFactory是從哪裏來的呢?能夠看一下Retrofit這個類的代碼能夠知道,是經過client和callFactoryide

這兩個方法賦值的,那怎麼才能攔截上面的newCall方法,固然是自定義CallFactory了。ui

/** * 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;
}
複製代碼

自定義CallFactorythis

難道咱們要所有重寫OkHttpClient裏面的方法?這裏固然不須要,咱們只須要代理一下newCall方法就OK了,核心代碼以下url

/** * 建立時間: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的處理比較簡單,開發者能夠本身實現和管理,這個方法是開放的。

是否是綠色、環保、無污染,代碼侵入性極低。

源碼地址: github.com/xchengDroid…

相關文章
相關標籤/搜索