Retrofit浅析

Retrofit源码
官方文档

介绍

A type-safe HTTP client for Android and Java.

官方给出的一句话描述:适用于Android和Java的类型安全的HTTP客户端。
是Android工程中使用率很高的网络请求框架。

使用

官方给出的使用样例

public final class SimpleService {
  public static final String API_URL = "https://api.github.com";

  public static class Contributor {
    public final String login;
    public final int contributions;

    public Contributor(String login, int contributions) {
      this.login = login;
      this.contributions = contributions;
    }
  }

  public interface GitHub {
  	// 用注解形式定义接口
    @GET("/repos/{owner}/{repo}/contributors")
    Call<List<Contributor>> contributors(@Path("owner") String owner, @Path("repo") String repo);
  }

  public static void main(String... args) throws IOException {
  	// 构建retrofit实例
    // Create a very simple REST adapter which points the GitHub API.
    Retrofit retrofit =
        new Retrofit.Builder()
            .baseUrl(API_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build();
	// 创建接口实例
    // Create an instance of our 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");
	// 执行请求
    // Fetch and print a list of the contributors to the library.
    List<Contributor> contributors = call.execute().body();
    for (Contributor contributor : contributors) {
      System.out.println(contributor.login + " (" + contributor.contributions + ")");
    }
  }
}

实现的功能是从github仓库中获取项目的贡献者名单,运行结果如下

JakeWharton (1062)
swankjesse (280)
pforhan (48)
eburke (36)
NightlyNexus (29)
dnkoutso (26)
edenman (24)
loganj (17)
Noel-96 (16)
rcdickerson (14)
rjrjr (13)
adriancole (9)
holmes (8)
Jawnnypoo (8)
JayNewstrom (7)
kryali (7)
swanson (7)
crazybob (6)
danrice-square (5)
vanniktech (5)
Turbo87 (5)
naturalwarren (5)
guptasourabh04 (4)
artem-zinnatullin (3)
chriscizek (3)
codebutler (3)
icastell (3)
jjNford (3)
ojh102 (3)
f2prateek (3)

从例子中可以看出,主要进行了如下操作:

  • 使用注解定义接口
  • 构建Retrofit实例
  • 创建接口实例
  • 获取调用实例
  • 执行请求

有了这个框架很容易就完成了网络请求,这不禁让人有些好奇,这就行了?

  • 注解定义的接口是怎么转换成请求的?
  • 请求是怎么发出去的?

原理

下面带着疑问尝试把整个流程梳理一下,基于2.9.0版本:
使用注解定义的接口,整个调用过程中一定有对注解的解析,这个暂时不单独特意去看了。

Retrofit构建-Retrofit.Builder().build()

构建Retrofit,从设计模式上看使用了建造者模式,看下build方法中干了什么, 下面代码中只保留了核心代码

    public Retrofit build() {
      ...
      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }

      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // Make a defensive copy of the adapters and add the default Call adapter.
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

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

      // Add the built-in converter factory first. This prevents overriding its behavior but also
      // ensures correct behavior when using converters that consume all types.
      converterFactories.add(new BuiltInConverters());
      converterFactories.addAll(this.converterFactories);
      converterFactories.addAll(platform.defaultConverterFactories());

      return new Retrofit(
          callFactory,
          baseUrl,
          unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories),
          callbackExecutor,
          validateEagerly);
    }

其中为构建Retrofit准备了几个参数

参数取值功能
callFactory如果调用者有传入则使用传入,否则使用OkHttpClient从名称上看这个应该是用来发起http请求调用的工厂对象
callbackExecutor如果调用者有传入则使用传入,否则使用默认执行器从名称上看是回调使用的执行器
callAdapterFactories是一个适配器工厂列表,会将传入的添加进去,并且会加入一个默认适配器工厂从名称上看是一组适配器工厂,应该是可以将调用转换为不同形式
converterFactories是一个转换器工厂列表,会加入一个内置的转换器工厂,加入传入的转换器工厂,再加入默认的转换器工厂从名称上看是一组转换器工厂,应该是用来做数据格式转换的

创建接口实例-retrofit.create()

  public <T> T create(final Class<T> service) {
    ...
    // 动态代理创建接口实例
    return (T)
        Proxy.newProxyInstance(
            service.getClassLoader(),
            new Class<?>[] {service},
            new InvocationHandler() {
              private final Platform platform = Platform.get();
              private final Object[] emptyArgs = new Object[0];

              @Override
              public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
                  throws Throwable {
                // 如果是Object中的方法,直接使用反射调用
                // If the method is a method from Object then defer to normal invocation.
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args);
                }
                args = args != null ? args : emptyArgs;
                // 判断是否是声明的默认方法(即使用default声明的方法),因为default方法不能使用常规的反射方法调用
                return platform.isDefaultMethod(method)
                    ? platform.invokeDefaultMethod(method, service, proxy, args)
                    : loadServiceMethod(method).invoke(args);
              }
            });
  }

create方法中使用了动态代理模式创建接口实例

获取调用实例-interface.method()

调用接口中方法实际调用的是InvocationHandler#invoke方法,该方法中对接口中声明的不同类型方法使用不同方式进行处理,不过常规情况下走到的几乎都是loadServiceMethod(method).invoke(args),这一句代码中时机包含了两步操作:

  1. 调用loadServiceMethod获取ServiceMethod实例
  2. 调用实例的invoke方法
  ServiceMethod<?> loadServiceMethod(Method method) {
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      // 从缓存中读取ServiceMethod实例
      result = serviceMethodCache.get(method);
      if (result == null) {
      	// 缓存中没有的话去创建ServiceMethod
        result = ServiceMethod.parseAnnotations(this, method);
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

创建ServiceMethod其中又包含了两步:

  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
  	// 1、生成RequestFactory实例
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
	...
	// 2、生成ServiceMethod实例
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }

生成RequestFactory是通过调用RequestFactory$Builder.build实现的,表面看是生成RequestFactory实例,实际上该方法会解析方法注解和参数注解

  • 解析方法注解:得到url路径、拿到路径中的参数名(如{owner})
  • 解析参数注解:将参数注解都转换为ParameterHandler对象,每个对象都会实现apply方法,在这个方法中,会调RequestBuilder对应的方法,用以向请求中添加参数,利用了装饰者模式。例如,ParameterHandler.Query#apply中调用了RequestBuilder#addQueryParam方法;ParameterHandler.Path#apply中调用了RequestBuilder#addPathParam方法。

生成ServiceMethod同样也没有那么简单

  • 获取CallAdapter:这里拿到的是按序遍历callAdapterFactories(构建Retrofit是被赋值)列表中的工厂创建的adapter,其中第一个返回类型和要求相符的adapter
  • 获取Converter:这里拿到的是按序遍历converterFactories(构建Retrofit是被赋值)列表中的工厂创建的converter中第一个符合要求的
  • 获取callFactory:这里拿到的是callFactory(构建Retrofit是被赋值)
  • 创建HttpServiceMethod:利用上面获取到的三个对象,加上传入的RequestFactory对象,去完成创建,不考虑kotlin的情况下,创建出来的对象是CallAdapted(继承关系CallAdapted -> HttpServiceMethod -> ServiceMethod)实例

上面说过loadServiceMethod(method).invoke(args)是调用ServiceMethod#invoke方法,invoke方法在HttpServiceMethod中被重写,并调用了抽象方法adapt

  @Override
  final @Nullable ReturnT invoke(Object[] args) {
    // 这里创建的OkHttpCall是重点关注对象
    Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    return adapt(call, args);
  }

而adapt又在CallAdapted中被重写, 并调用callAdapter.adapt方法,callAdapter的来源在生成ServiceMethod的过程中已经提到了

  static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {
    private final CallAdapter<ResponseT, ReturnT> callAdapter;

    CallAdapted(
        RequestFactory requestFactory,
        okhttp3.Call.Factory callFactory,
        Converter<ResponseBody, ResponseT> responseConverter,
        CallAdapter<ResponseT, ReturnT> callAdapter) {
      super(requestFactory, callFactory, responseConverter);
      this.callAdapter = callAdapter;
    }

    @Override
    protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
      return callAdapter.adapt(call);
    }
  }

那么loadServiceMethod(method).invoke(args)实际调用的是callAdapter.adapt(call),这里使用了适配器模式
在构建Retrofit时如果没有传入默认使用的是DefaultCallAdapterFactory创建的callAdapter

final class DefaultCallAdapterFactory extends CallAdapter.Factory {
  private final @Nullable Executor callbackExecutor;

  DefaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
    this.callbackExecutor = callbackExecutor;
  }
  // 调用get方法用来创建CallAdapter
  @Override
  public @Nullable CallAdapter<?, ?> get(
      Type returnType, Annotation[] annotations, Retrofit retrofit) {
    ...
    // 注解中如果不包含@SkipCallbackExecutor则使用传入的callbackExecutor
    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) {
      	// 注解包含@SkipCallbackExecutor使用传入的call(即OkHttpCall),否则使用ExecutorCallbackCall
        return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
      }
    };
  }
  ...
}

ExecutorCallbackCall使用了代理模式,对call进行代理,在call的成功、失败方法中加入对executor的回调,这种方式可用来切换线程,到这里可以知道得到的call
实际就是OkHttpCall

执行请求-call.execute()

注意:到这里为止的Call都是Retrofit中的Call类型(retrofit2.Call),下面还会涉及到okhttp3.Call

  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());
  }

依然可以分成两部分

  1. 获得原始请求调用
  2. 执行请求
  3. 解析响应内容

原始请求构造

OkHttpCall中构造原始请求调用的方法如下

  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是在Retrofit.Builder().build()时被赋值的,默认是okhttp3.OkHttpClient, 是okhttp3中的一个类,内部逻辑暂且不看,知道newCall是用来生成okhttp3.Call即可,并且需要传入一个okhttp3.Request类型的参数,这个参数是通过RequestFactory#create得到的

  okhttp3.Request create(Object[] args) throws IOException {
    @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
    // 前面提到过ParameterHandler是解析注解后得到的参数对象
    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
	...
    RequestBuilder requestBuilder =
        new RequestBuilder(
            httpMethod,
            baseUrl,
            relativeUrl,
            headers,
            contentType,
            hasBody,
            isFormEncoded,
            isMultipart);
	...
    List<Object> argumentList = new ArrayList<>(argumentCount);
    for (int p = 0; p < argumentCount; p++) {
      argumentList.add(args[p]);
      // 调用applay会把相应的请求参数添加到requestBuilder中
      handlers[p].apply(requestBuilder, args[p]);
    }
	// 构造一个http请求
    return requestBuilder.get().tag(Invocation.class, new Invocation(method, argumentList)).build();
  }

过程中会把解析注解得到的参数添加到request中,并将构造的request返回

执行请求

执行逻辑同样在okhttp3中,调用的是okhttp3.Call#execute,不过okhttp3.Call是个接口,实现类是okhttp3.RealCall

解析响应内容

  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) {
      rawBody.close();
      return Response.success(null, rawResponse);
    }

    ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
    try {
      // 使用构造Retrofit时设置的转换器进行数据转换
      T body = responseConverter.convert(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;
    }
  }

过程分为三步:

  1. 根据返回码判断请求是否成功
  2. 进行数据格式转换(200<=code<=203才会执行)
  3. 根据响应内容构造一个响应对象并返回

梳理下整个流程的调用关系
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值