Retrofit反射修改baseUrl

霸气的名字 2020-06-23

背景:Android开发时,需要两个平台切换,项目中网络请求是用的Retrofit这块,发现没有直接切换的接口,百度一下,大部分直接是通过拦截器里面进行切换,说Retrofit中baseUrl是final类型,不能反射修改。通过测试后发现final值是可以修改的(包括基本类型),但是final值的初始化要在构造方法中赋值。

 解决思路:

  +. 直接替换掉Retrofit中的baseUrl

public final class Retrofit {
  private final Map<Method, ServiceMethod<?, ?>> serviceMethodCache = new ConcurrentHashMap<>();

  final okhttp3.Call.Factory callFactory;
  final HttpUrl baseUrl;
  final List<Converter.Factory> converterFactories;
  final List<CallAdapter.Factory> callAdapterFactories;
  final @Nullable Executor callbackExecutor;
  final boolean validateEagerly;

    反射替换代码如下:

//baseUrl:切换的url
   //retrofit:对于的Retrofit实例
   HttpUrl httpUrl = HttpUrl.parse(baseUrl);
   Field field = retrofit.getClass().getDeclaredField("baseUrl");
   field.setAccessible(true);
   field.set(retrofit, httpUrl);

   单点可以跟踪查看,retrofit中的baseUrl确实已经替换成功,但是去请求数据时,发现还是之前的数据。

   

   具体原因跟踪源码:

ServiceMethod<?, ?> loadServiceMethod(Method method) {
    ServiceMethod<?, ?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = new ServiceMethod.Builder<>(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

   

    Retrofit会根据Method缓存请求,如果之前请求过,就是你切换了baseUrl,请求同个Method,那还是之前的,对于这样,把对应的缓存清理下就行。

field = retrofit.getClass().getDeclaredField("serviceMethodCache");
 field.setAccessible(true);
 Map serviceMethodCache = (Map) field.get(retrofit);
 serviceMethodCache.clear();

    再测试下,可以成功切换。

    完整代码如下:

public static void baseUrl(Retrofit retrofit, String baseUrl) throws Exception {
        HttpUrl httpUrl = HttpUrl.parse(baseUrl);
        Field field = retrofit.getClass().getDeclaredField("baseUrl");
        field.setAccessible(true);
        field.set(retrofit, httpUrl);

        field = retrofit.getClass().getDeclaredField("serviceMethodCache");
        field.setAccessible(true);
        Map serviceMethodCache = (Map) field.get(retrofit);
        serviceMethodCache.clear();
    }

   当网络请求正在进行时,这种暴力切换方式,可能会有些意想不到的问题,至于其它问题,后续发现追加。

    

相关推荐