­

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

  • 2019 年 10 月 4 日
  • 筆記

需求場景

在使用retrfoit做網路請求開發的時候,如果app涉及到多個不同 BaseUrl,僅僅是baseUrl不同,retrofit的其他配置都是一樣的,我們不得不創建管理多個retrofit實例,或者需要在Service介面處修改@Get @Url等傳入完整的url地址。這其實不是我們所期望的,因為如果有很多不同baseurl 地址的請求,我們可能某個baseurl只有一個或者很少的service使用的到,亦或者很多介面需要頻繁的調用。我們不期望管理多個retrofit實例,是否可以只創建一個retrofit對象就能解決全部問題呢?答案肯定是可以的。

一幫的解決方案

  • 配置多個retrofit對象
  • okhttp攔截器Interceptorintercept(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