Retrofit源码分析

127 阅读4分钟

Retrofit通过对okHttp进行再次封装,简化了网络实现。

1.代码使用

  • 创建Retrofit对象
  • 创建请求接口代理对象
  • 反射生成okHttpCall对象
  • 请求和解析ResponseBody返回
Retrofit retrofit = 
   new Retrofit.Builder()
   .baseUrl()
   .addConverterFactory()
   .build();
   
// create an instance of out github API interface.
GitHub github = retrofit.create(GitHub.class);

// create a call instance for looking up Retrofit contributors.
Call<List<Contributor>> call = github.contributors("square", "retrofit");

List<Contributor> contributors = call.execute().body();

2.创建Retrofit对象

建造者模式创建Retrofit对象

build,首先retrofit会新建一个call,其实质就是okHttp,作用就是网络请求器;

默认callAdapter是ExecutorCallAdapterFactory,其实质就是网络请求器call的适配器,而在Retrofit中call就是指okhttp,callAdapter就是用来将okhttp适配给不同的平台的;

Retrofit提供了四种callAdapter:ExecutorCallAdapterFactory,GuavaCallAdapterFactory,Java8CallAdapterFactory,RxJavaCallAdapterFactory;

提供适配器的目的是易于扩展,只需要添加即可使用,使用RxJava的适配器后,功能更加强大 Retrofit负责接收配置的功能然后进行对象的初始化,这个也就是Build模式屏蔽掉创建对象复杂过程的好处;

functiondescription
client指定要创建的自定义okHttpClient
callFactory指定要创建的自定义调用工厂
baseUrl配置服务器地址,指定要创建的基类url,一般包含协议部分、域名部分、端口部分
addConverterFactory添加用于对象序列化和反序列化的转换器工厂,将http返回的数据解析成java对象,常见的有xml,gson等;GsonConverter就是将gson数据转换成我们的java对象,而不用重新去解析这些gson数据
addCallAdapterFactory添加一个支持服务方法返回类型的调用适配器工厂,callAdapter同样被一个list维护,用户可以添加多个callAdapter
callbackExecutor添加call执行的线程池
validateEagerly实例化接口对象完成立即验证提供的接口中所有方法的配置

3.创建请求接口代理对象

  1. validateServiceInterface检查接口类及其父类们是否为泛型类,如果是就抛异常;
  2. 下面就是retrofit核心技术点动态代理:利用 Proxy 请求接口类,创建 InvocationHandler 的实例对象,用来处理被代理类的所有请求方法调用;
public <T> T create(final Class<T> service) {
  validateServiceInterface(service);
  return (T)
     Proxy.newProxyInstance(
         service.getClassLoader(),
         new Class<?>[] {service},
         new InvocationHandler() {
           private final Object[] emptyArgs = new Object[0];
           
           @Override
           public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args) 
               throws Throwable {
             if (method.getDeclaringClass() == Object.class) {
                 return method.invoke(this, args);
              }
              args = atgs != null ? args : emptyArgs;
              Platform platform = Platform.get();
              return platform.isDefaultMethod(method)
                  ? platform.invokeDefaultMethod(method, service, proxy, args)
                  : loadServiceMethod(method).invoke(args);
              }
           }); 
        

平台是 android29 环境,所以 get 方法返回的实例是 Android24;

private static final class Android24 extends Platform {
    static boolean isSupported() {
      return Build.VERSION.SDK_INT >= 24;
    }
    
    private @Nullable Constructor<lookup> lookupConstructor;
    
    @Override
    Executor defaultCallbackExecutor() {
       return MainThreadExecutor.INSTANCE;
    }   
    
    @Override
    List<? extends CallAdapter.Factory> createDefaultCallAdapterFactories (@Nullable Executor callbackExecutor) {
    return asList(
        new CompletableFutureCallAdapterFactory(),
        new DefaultCallAdapterFactory(callbackExecutor));
    }
    ...
}    

4.生成OkHttpCall对象

  • 反射解析接口方法注解,参数注解生成CallAdapted;
  • 调用invoke方法生成okHttpCall,该invoke方法中创建了一个okHttpCall对象,并调用了adapt(call, args) 方法;
  • 创建代理类ExecutorCallbackCall代理okHttpCall;

image.png

4.1反射解析接口方法注解参数注解获得请求类型和urlpath等

需要得到如下信息供Retrofit使用获得方法注解数组methodAnnotations,参数类型数组parameterTypes,参数注解数组patameterAnnotationsArray

Build(Retrofit retrofit, Method method) {
  this.retrofit = retrofit;
  this.method = method;
  this.methodAnnotations = method.getAnnotations();
  this.parameterTypes = method.getGenericParameterTypes();
  this.parameterAnnotationsArray = method.getParameterAnnotations();
}

解析方法注解获得请求类型httpMethod和url路径relativeUrl;

private void parseMethodAnnotation(Annotation annotation) {
   ...
   else if (annotation instanceof GET) {
      parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
   }
   ...
}   

正则匹配url获得path字符串集合赋值给relativeUrlParamNames;

static Set<String> parsePathParameters(String path) {
    Matcher m = PARAM_URL_REGEX.matcher(path);
    Set<String> patterns = new LinkedHashSet<>();
    while (m.find()) {
      patterns.add(m.group(1));
    }
    return patterns;
}    

生成方法parameterHandler后面用来替换relativeUrl中的path;

RequestFactory build() {
   ...
   int parameterCount = parameterAnnotationsArray.length;
   parameterHandlers = new ParameterHandler<?>[parameterCount];
   for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
       parameterHandlers[p] = parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
   }
   ...
   return new RequestFactory(this);
}   

生成CallAdapter对象;

前文已经根据android29环境生成了Platform.Android24,再结合retrofit.build方法,根据请求接口类方法返回值是Call,创建defaultCallAdapterFactory.get()的callAdapter对象和GsonResponseBodyConverter.responseBodyConverter的GsonResponseBodyConverter对象;

callFactory是用的默认创建的okhttpClient

abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {
   static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(Retrofit retrofit, Method method, RequestFactory requestFactory) {
      ...
      CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method, adapterType, annotations);
      ...
      Converter<ResponseBody, ResponseT> responseConverter = createResponseConverter(retrofit, method, responseType);
      okhttp3.Call.Factory callFactory = retrofit.callFactory;
      if (!isKotlinSuspendFunction) {
         return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
      }
      ...
   }
}   

4.2调用invoke方法生成okHttpCall

static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {
   ...
   @Override
   protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
      return callAdapter.adapt(call);
   }
   
   @Override
   final @Nullable ReturnT invoke(Object[] args) {
     Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
     return adapt(call, args);
     }
   }
     

4.3创建代理类ExecutorCallbackCall代理okHttpCall

Platform.Android24生成了一个MainThreadExecutor.INSTANCE,所以adapt会生成ExecutorCallbackCall对象;

final class DefaultCallAdapterFactory extends CallAdapter.Factory {
   private final @Nullable Executor callbackExecutor;
   ...
   @Override
   public @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
      if (getRawType(returnType) != Call.class) {
         return null;
      }
      if (!(returnType instanceOf ParameterizedType)) {
      throw new IllegalArgumentException(
          "Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
      }
      final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);
      
      final Executor executor = 
          Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
              ? null
              : callbackExecutor;
      return new CallAdapter<Object, Call<?>>() {
          @Override
          public Type responseType() {
              return responseType;
          }
          
          @Override
          public Call<Object> adapt(Call<Object> call) {
              return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
          }
          
          @Override
          public Call<Object> adapt(Call<Object> call) {
               return executor == null ? Call : new ExecutorCallbackCall<>(executor, call);
          }
       };
   }
}   

android中一般情况下都是在UI线程完成UI操作,但是okhttp没有这个能力,所以作者用静态代理模式,实现了enqueue回调从子线程切换到UI线程;

static final class ExecutorCallbackCall<T> implements Call<T> {
   ...
   @Override
   public void enqueue(final Callback<T> callback) {
      objects.requireNonNull(callback, "callback == null");
      delegate.enqueue(
          new Callback<T>() {
             @Override
             public void onResponse(Call<T> call, final Response<T> response) {
                callbackExecutor.execute(
                   () -> {
                     if (delegate.isCanceled()) {
                        callback.onFailure(ExecutorCallback.this, new IOException("Canceled"));
                     } else {
                        callback.onResponse(ExecutorCallbackCall.this, response);
                     }
                    });
             }
             
             @Override
             public void onFailure(Call<T> call, final Throwable t) {
                 callbackExecutor.execute(() -> callback.onFailure(ExecutorCallbackCall.this, t));
             }
           });
}
...
@Override
public Response<T> execute() throws IOException {
    return delegate.execute();
 }
 ...
}

目前除了生成ExecutorCallbackCall对象还在RequestFactory存储了如下值

参数名来源途径
httpMethod接口方法注解名GET
baseUrl自定义传入api.github.com/
relativeUrl接口方法注解值/repos/{owner}/{repo}/contributors
ParameterHandlerRequestFactory方法 parseParameterAnnotation2个ParameterHandler$Path name参数一个是owner一个是repo

5.请求和解析ResponseBody返回

  • 生成okhttp3.Requset对象,调用okhttpClient请求
  • 使用前面生成的responseConverter解析ResponseBody

image.png

5.1调用okhttpClient请求,调用okhttpClient请求

final class okHttpCall<T> implements Call<T> {
    ...
    @Override
    public Response<T> execute() throws IOException {
       okhttp3.Call call;
       
       synchronized (this) {
          if (executed) throw new IllegalStateException("Already executed.");
          executed = true;
          
          call = getRawCall();
       }
       
       if (canceled) {
          call.cancel();
       }
       
       return parseResponse(call.execute());
    }
    
    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;
   }
   ...
}  

5.2Gson解析ResponseBody

final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
   private final Gson gson;
   private final TypeAdapter<T> adapter;
   
   GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
      this.gson = gson;
      this.adapter = adapter;
   }
   
   @Override
   public T convert(ResponseBody value) throws IOException {
      JsonReader jsonReader = gson.newJsonReader(value.charStream());
      try {
         T result = adapter.read(jsonReader);
         if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
            throw new JsonIOException("JSON document was not fully consumed.");
         }
         return result;
      } finally {
         value.close();
      }
   }
}