Retrofit source code reading record

image-20210819152814979

Retrofit instantiation

Generate instances from the builder's schema.

  • platform: platform type, for different compatibility

  • callFactory: used to create a method call model Call (asynchronous or synchronous, a bit like FutureTask, used to obtain the result call), and convert the method call into Http access.

  • baseUrl: base address. It generally represents a domain name or IP, and the identification domain name must be included at the end \to ensure the accuracy of the path. \Normal mode is optimized if host + path occurs twice .

  • converterFactories: Serialization and deserialization factories, used to obtain Converter, each factory has a custom supported type;

    When setting the platform, the built-in will be set BuiltInConvertersto ensure the correctness of basic type conversion.

    final class BuiltInConverters extends Converter.Factory {
          
          
      @Override
      public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
          Retrofit retrofit) {
          
          
        if (type == ResponseBody.class) {
          
          
          if (Utils.isAnnotationPresent(annotations, Streaming.class)) {
          
          
              // 原样返回
            return StreamingResponseBodyConverter.INSTANCE;
          }
            // 将结果缓存到 buffer 中,避免后续的IO
          return BufferingResponseBodyConverter.INSTANCE;
        }
        if (type == Void.class) {
          
          
            // 用来直接关闭连接,返回 null
          return VoidResponseBodyConverter.INSTANCE;
        }
        return null;
      }
     }
    
  • adapterFactories: Call the model adapter factory to get the CallAdapter (which supports the conversion of calls into specific types), and each factory has a custom supported type. It is the outer layer of Converter, which executes the call first, and then converts it to the result model according to Converter.

    Adapter is more concerned with the behavior pattern of the call.

  • callbackExecutor: Call the executor, the key to whether it is asynchronous.

  • validateEagerly: Whether to parse and initialize the calling method in advance

The default configuration is OkHttpClient, empty Executor, default return interface adapter (just do nothing, return as it is), no serialization deserializer.

public static final class Builder {
    
    
  private Platform platform;
  private okhttp3.Call.Factory callFactory;
  private HttpUrl baseUrl;
  private List<Converter.Factory> converterFactories = new ArrayList<>();
  private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
  private Executor callbackExecutor;
  private boolean validateEagerly;
  public Retrofit build() {
    
    
      if (baseUrl == null) {
    
    
        throw new IllegalStateException("Base URL required.");
      }

      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
    
    
          // 默认就是 OkHttpClient,一家的
        callFactory = new OkHttpClient();
      }

      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
    
    
          // Java 的话,默认是 NULL
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // Make a defensive copy of the adapters and add the default Call adapter.
      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
      // 如果 callbackExecutor != null,那实际是做一层同步转异步调用的封装
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

      // Make a defensive copy of the converters.
      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
    }
}
Platform

Platform guarantees the adaptability of each platform, mainly through whether the load special class will report an error.

private static Platform findPlatform() {
    
    
  try {
    
    
    Class.forName("android.os.Build");
    if (Build.VERSION.SDK_INT != 0) {
    
    
      return new Android();
    }
  } catch (ClassNotFoundException ignored) {
    
    
  }
  try {
    
    
    Class.forName("java.util.Optional");
    return new Java8();
  } catch (ClassNotFoundException ignored) {
    
    
  }
  try {
    
    
    Class.forName("org.robovm.apple.foundation.NSObject");
    return new IOS();
  } catch (ClassNotFoundException ignored) {
    
    
  }
  return new Platform();
}

It mainly ensures whether the interface default method, Executor, etc. need to be processed.

The default implementation classes are Java, Android, and IOS.

image-20210819101521333

interface

At initialization time, an interface proxy is generated

validateEagerly == true

At the time of initialization, all non-default methods are pulled to generate method proxy (ServiceMethod), and cached. Otherwise defer to actual call to actual.

The actual method call is first packaged by Okhttp to ensure the processing of the network call; then it will be packaged by CallAdapter (find whether it is applicable from Retrofit) to make the actual call (usually OkHttp Client).

public <T> T create(final Class<T> service) {
    
    
  Utils.validateServiceInterface(service);
  if (validateEagerly) {
    
    
    eagerlyValidateMethods(service);
  }
    //接口代理类
  return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] {
    
     service },
      new InvocationHandler() {
    
    
        private final Platform platform = Platform.get();

        @Override public Object invoke(Object proxy, Method method, Object... args)
            throws Throwable {
    
    
          // If the method is a method from Object then defer to normal invocation.
          if (method.getDeclaringClass() == Object.class) {
    
    
            return method.invoke(this, args);
          }
          if (platform.isDefaultMethod(method)) {
    
    
              // 只有 Java 平台才能执行 default 方法
            return platform.invokeDefaultMethod(method, service, proxy, args);
          }
            // 加载方法代理
          ServiceMethod serviceMethod = loadServiceMethod(method);
            // 包装成 Http 调用
          OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            // 将调用模型封装为特定的模型,比如 java8 CompletedFuture、rxJava 的响应式等
            // 一般默认是 DefaultCallAdapterFactory,不做任何处理
            // 或自定义线程池情况下,ExecutorCallAdapterFactory 转为异步调用
          return serviceMethod.callAdapter.adapt(okHttpCall);
        }
      });
}

The final result may be the result you want, or it may be just a package of Http calls. It depends on what the CallAdapter does.

The purpose of doing this, I think, is also to do some processing in addition to the basic http request, such as the Hystrix fuse processing used by our company.

Of course, you can also customize Adapter and AdapterFactory, and directly call them when doing Call adaptation, and generate business results.

A JavaAdapter is sort of like this, but returns the result in a CompletedFuture.

Retrofit - Page 1

ServiceMethod

A layer of packaging for the method, the most important thing is to parse the annotations and metadata information about Retrofit on the method, and then generate a set of parsers, converters, etc.

When actually calling, convert the actual parameters into Http Request; if the call is successful, then convert the result into a specific business type.

Request toRequest(Object... args) throws IOException {
    
    }
T toResponse(ResponseBody body) throws IOException {
    
    }
static final class Builder<T> {
    
    
   // 基础属性, 借助 retrofit 的一些设置,以及 Method 的其他信息
  final Retrofit retrofit;
  final Method method;
  final Annotation[] methodAnnotations;
  final Annotation[][] parameterAnnotationsArray;
  final Type[] parameterTypes;
// 解析方法的元数据
  Type responseType;
  boolean gotField;
  boolean gotPart;
  boolean gotBody;
  boolean gotPath;
  boolean gotQuery;
  boolean gotUrl;
  String httpMethod;
  boolean hasBody;
  boolean isFormEncoded;
  boolean isMultipart;
  String relativeUrl;
  Headers headers;
  MediaType contentType;
  Set<String> relativeUrlParamNames;
  ParameterHandler<?>[] parameterHandlers;
  Converter<ResponseBody, T> responseConverter;
  CallAdapter<?> callAdapter;

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

build

The instantiation method is also the Builder mode.

public ServiceMethod build() {
    
    
   // 根据返回类型和注解,在 Retrofit 配置的列表中找到匹配的 callAdapter
   // 默认的请求方法返回值都是Call<T>, 可以继承CallAdapter.Factory来实现自定义的返回值类型
  callAdapter = createCallAdapter();
  responseType = callAdapter.responseType();
  if (responseType == Response.class || responseType == okhttp3.Response.class) {
    
    
    throw methodError("'"
        + Utils.getRawType(responseType).getName()
        + "' is not a valid response body type. Did you mean ResponseBody?");
  }
   // 根据返回类型和注解,在 Retrofit 配置的列表中找到匹配的 converter
  responseConverter = createResponseConverter();

  for (Annotation annotation : methodAnnotations) {
    
    
      // 处理注解,来填充元数据信息,比如 GET、PUT、isMultipart、isFormEncoded
      // 如果出现不符合的情况,直接报错
    parseMethodAnnotation(annotation);
  }
 // 省略一些异常情况的检查
  。。。。。

  int parameterCount = parameterAnnotationsArray.length;
    // 负责将参数填充到对应 Http 的请求模型中,比如 Body、Header、URLParam等
  parameterHandlers = new ParameterHandler<?>[parameterCount];
  for (int p = 0; p < parameterCount; p++) {
    
    
    Type parameterType = parameterTypes[p];
      // 不能处理泛型
    if (Utils.hasUnresolvableType(parameterType)) {
    
    
      throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
          parameterType);
    }

    Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
    if (parameterAnnotations == null) {
    
    
      throw parameterError(p, "No Retrofit annotation found.");
    }
	// 解析注解内容,生成对应的参数处理器
    parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
  }

  // 省略参数解析的异常检查
......
  return new ServiceMethod<>(this);
}

OkHttpCall

Retrofit converts a method call into an Http call through the Call interface, which can be a synchronous call to execute or an asynchronous call to enqueue (Callback).

public interface Call<T> extends Cloneable {
    
    
  Response<T> execute() throws IOException;
  void enqueue(Callback<T> callback);
  Request request();

Callback actually reflects the callback, built-in success or failure processing.

Like when using a JavaAdapter, the result is converted to a CompletedFuture.

Generally speaking, in the default implementation of retrofit, enqueue is used when there is a thread pool.

Finally, a Call is obtained. As long as the execution method is called, an Http request will be generated and the result will be obtained.

Retrofit - page 2

Build Request and OkHttp Call
private okhttp3.Call createRawCall() throws IOException {
    
    
    // 像前面说的,通过解析器、转换器等,将参数转为 Request 参数
  Request request = serviceMethod.toRequest(args);
    // 构建 okhttp3 的调用,毕竟使用 OkHttp 请求的。
  okhttp3.Call call = serviceMethod.callFactory.newCall(request);
  if (call == null) {
    
    
    throw new NullPointerException("Call.Factory returned null.");
  }
  return call;
}

public synchronized Request request() {
    
    
    okhttp3.Call call = rawCall;
    if (call != null) {
    
    
      return call.request();
    }
    if (creationFailure != null) {
    
    
      if (creationFailure instanceof IOException) {
    
    
        throw new RuntimeException("Unable to create request.", creationFailure);
      } else {
    
    
        throw (RuntimeException) creationFailure;
      }
    }
    try {
    
    
      return (rawCall = createRawCall()).request();
    } catch (RuntimeException e) {
    
    
      creationFailure = e;
      throw e;
    } catch (IOException e) {
    
    
      creationFailure = e;
      throw new RuntimeException("Unable to create request.", e);
    }
  }
synchronous execute
public Response<T> execute() throws IOException {
    
    
  okhttp3.Call call;

  synchronized (this) {
    
    
    if (executed) throw new IllegalStateException("Already executed.");
    executed = true;

    if (creationFailure != null) {
    
    
        // 说明之前构建过 Call 和 Request,且失败了
      if (creationFailure instanceof IOException) {
    
    
        throw (IOException) creationFailure;
      } else {
    
    
        throw (RuntimeException) creationFailure;
      }
    }

    call = rawCall;
    if (call == null) {
    
    
      try {
    
    
        call = rawCall = createRawCall();
      } catch (IOException | RuntimeException e) {
    
    
        creationFailure = e;
        throw e;
      }
    }
  }

  if (canceled) {
    
    
    call.cancel();
  }
 // 执行返回结果
  return parseResponse(call.execute());
}
asynchronous enqueue
public void enqueue(final Callback<T> callback) {
    
    
  if (callback == null) throw new NullPointerException("callback == null");

  okhttp3.Call call;
  Throwable failure;

  synchronized (this) {
    
    
   // 同上

  call.enqueue(new okhttp3.Callback() {
    
    
    @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
        throws IOException {
    
    
      Response<T> response;
      try {
    
    
          // 成功,转换结果
        response = parseResponse(rawResponse);
      } catch (Throwable e) {
    
    
          // 失败流程
        callFailure(e);
        return;
      }
          // 成功流程
      callSuccess(response);
    }

    @Override public void onFailure(okhttp3.Call call, IOException e) {
    
    
      try {
    
    
        callback.onFailure(OkHttpCall.this, e);
      } catch (Throwable t) {
    
    
        t.printStackTrace();
      }
    }

    private void callFailure(Throwable e) {
    
    
      try {
    
    
        callback.onFailure(OkHttpCall.this, e);
      } catch (Throwable t) {
    
    
        t.printStackTrace();
      }
    }

    private void callSuccess(Response<T> response) {
    
    
      try {
    
    
        callback.onResponse(OkHttpCall.this, response);
      } catch (Throwable t) {
    
    
        t.printStackTrace();
      }
    }
  });
}
Convert result parseResponse

Convert OkHttp Response to Retrofit's own Response

Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    
    
  ResponseBody rawBody = rawResponse.body();

  // Remove the body's source (the only stateful object) so we can pass the response along.
  rawResponse = rawResponse.newBuilder()
      .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
      .build();

  int code = rawResponse.code();
  if (code < 200 || code >= 300) {
    
    
    try {
    
    
      // Buffer the entire body to avoid future I/O.
      ResponseBody bufferedBody = Utils.buffer(rawBody);
      return Response.error(bufferedBody, rawResponse);
    } finally {
    
    
      rawBody.close();
    }
  }

  if (code == 204 || code == 205) {
    
    
    return Response.success(null, rawResponse);
  }

  ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
  try {
    
    
    T body = serviceMethod.toResponse(catchingBody);
    return Response.success(body, rawResponse);
  } catch (RuntimeException e) {
    
    
    // If the underlying source threw an exception, propagate that rather than indicating it was
    // a runtime exception.
    catchingBody.throwIfCaught();
    throw e;
  }
}

Converter、Adapter

Corresponding extension packages can be introduced to support different scenarios.

Such as Jackson, Gson, FastJson Converter;

For example, Adapter that supports Java8 CompletedFuture and RxJava responsive programming.

Adapter judges by return value type by default, as long as it is adapted to a specific type. This type is not a business result model, but an invocation model.

Generally, it is Call, and Java8 is CompletedFuture, and the result is obtained through CompletedFuture.get.

Guess you like

Origin blog.csdn.net/jiangxiayouyu/article/details/119803290