Retrofit 源码深入分析 —— Call 对象的诞生与请求

一、概述

Retrofit 是一个基于 OkHttp ,并遵循 Restful 风格的一个网络请求封装库,它将一个请求以接口的形式具现化的表现出来,将 OkHttp 的请求操作简化,并可以配置 convert 将响应转换为你想要的数据 model,且支持异步或同步的操作,2.6.x 版本以上更是对协程进行了支持,其优异的设计,简便的请求方式,推出没多久就在 Android 开发领域出圈,目前 github star 数量已经达到 37.8k 并被大量的 App 采用。
现如今更是成为各大公司面试的长问问题。基于此,本篇文章会从源码层面深入分析 Retrofit 的运行机制,了解 Retrofit 的内部原理。

站在巨人的肩膀上眺望,就算最终我们成不了巨人,起码也能比旁人看的更远不是。

二、Retrofit 基本流程图

Retrofit 流程图

如图所示,接下来我们就按照流程图来逐步分析 Retrofit 的源码

三、一个简单的请求

阅读源码的最好方式就是从使用的角度从调用链一步步深入分析,这样能让我们避免在繁复纷杂的代码中迷失方向,无所适从。所以让我们从一个简单的请求开始,慢慢的揭开 Retrofit 神秘的面纱。

public interface GitHub {

 @GET("/repos/{owner}/{repo}/contributors") 
 Call> contributors(@Path("owner") String owner, @Path("repo") String repo);
 
}

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

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> call = github.contributors("square", "retrofit");

// Fetch and print a list of the contributors to the library.
List contributors = call.execute().body();

for (Contributor contributor : contributors) {
  System.out.println(contributor.login + " (" + contributor.contributions + ")");
}

上述代码是 Retrofit 官方 sample 中的例子,代码并不是很多,可以看到经过一些耳熟能详的构建步骤和一个声明的 Restful 接口,就获得了一个可以进行 enqueue/sync 请求的 Call 对象,而通过这个 Call 对象我们就可以直接获取我们想要的 Response, 不得不佩服,真的是将网络请求变得非常简便。

这里我特别说明一下 build 的构建过程,有助于后面文章的理解

public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }

      //第一步创建一个 OkHttpClient 对象
      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }

      //第二步创建一个回调线程 ,Platform 是在 Builder 构造方法中进行的实例化并传入
      //主要作用判断当前的运行环境,如果是 Android 则返回 AndroidCallback
      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // 第三步将所有的 CallAdapter add 到 callAdapterFactories 集合中
      //例如常用的 RxJavaCallAdapterFactory
      List callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

      // 第四步将所有的 Converter add 到 converterFactories 集合中
      //列如我们常用的 GsonConvertFactory
      List converterFactories = new ArrayList<>(
          1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());

      //添加 Retrofit 默认提供的 converter ,当我们的返回类型为 Call 就会使用这个 BuiltInConverters
      converterFactories.add(new BuiltInConverters());
      converterFactories.addAll(this.converterFactories);
      converterFactories.addAll(platform.defaultConverterFactories());

      //创建 Retrofit 实例
      return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
    }

说实话,前几年第一次看到这种网络请求的方式的时候,感觉还是蛮秀的,竟然还能这么玩? 在使用了一段时间后,非常好奇它是怎做到的,相信有不少童鞋应该也很好奇吧。接下来我们慢慢看看这么神奇的操作是怎么完成的~

四、Retrofit 的 create 方法

在进行分析之前,如果你对 Java 动态代理、反射、注解没什么了解的话,建议先简单的了解一下,否则可能无法愉快的阅读后续的文章。

public  T create(final Class service) {
    //校验接口是否可用
    validateServiceInterface(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 {
            // 调用的是成员类方法
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            //调用的是 Object 的默认方法
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            //将解析的请求组装成一个 Call 对象返回
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          }
        });
  }

create 方法接收一个 Class 类型的形参,内部通过动态代理的方式,在运行时创建了 Github Api 的接口实现类,在调用接口方法时,代理类会执行 InvocationHandler 的 invoke 方法,而在 invoke 方法中我们就可以对 Github 接口中声明的方法进行一些我们想做的改动了。

可能说到这里,还是有点绕,其实简而言之,这个 invoke 方法有点相当一个方法拦截器,可以在调用实际的方法前植入一些前期操作,比如当我们调用下面的代码时

Call> call = github.contributors("square", "retrofit");

此时在执行 contributors 方法时,会先调用 InvocationHandler 的 invoke方法,在这个方法中,Retrofit 会对方法中的注解通过反射进行解析,其中会解析形参类型,返回类型等等,并将其组装成一个 Call 对象返回。其实如果你对 Java 的 AOP 有过了解的话,那么这里的思想也是一样的。

动态代理相关内容就先解释到这里,接下来我们来重点看看 loadServiceMethod 方法

ServiceMethod loadServiceMethod(Method method) {
    //检查缓存中是否有可用的接口方法
    ServiceMethod result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        //解析接口方法中注解
        result = ServiceMethod.parseAnnotations(this, method);
        //加入缓存
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

loadServiceMethod 做的事情并不复杂,就是判断方法签名是否缓存过,没有则解析方法中的所有注解,并加入缓存,代码如下

abstract class ServiceMethod {
  static  ServiceMethod parseAnnotations(Retrofit retrofit, Method method) {
    //解析接口方法中的所有注解并组装成一个请求工厂
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

    //获取接口方法的返回类型
    Type returnType = method.getGenericReturnType();
    
    //省略一些判断代码...
    
                   //这里对各种参数进行检查,并拼装成一个 CallAdapted 对象
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }

  abstract @Nullable T invoke(Object[] args);
}

从上面的代码中我们了解到,ServiceMethod 是一个抽象类,内部声明了一个静态的泛型方法 parseAnnotations ,其中通过 RequestFactory 的静态 parseAnnotations 方法解析方法中的所有注解,通过 HttpServiceMethod 的静态 parseAnnotations 方法将解析的各种参数拼装成一个 CallAdapted 对象返回。

这里说明一下
HttpServiceMethod 也是个抽象类并继承自 ServiceMethod 类且实现了 invoke 方法。

简单的了解了上面的代码,接下来我们在逐一看看 RequestFactory.parseAnnotations 和 HttpServiceMethod.parseAnnotations 为我们做了什么

五、Request 的参数解析过程

RequestFactory.parseAnnotations 方法在将 retrofit 对象和 method 传入后,会创建一个 Builder 对象,主要作用就是赋值,如下

static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
    return new Builder(retrofit, method).build();
}

Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      this.methodAnnotations = method.getAnnotations();//方法的注解数组
      this.parameterTypes = method.getGenericParameterTypes();//形参的类型数组
      this.parameterAnnotationsArray = method.getParameterAnnotations();//形参的注解数组
}

而具体的解析则在 build 方法中进行,这里在特别说明一下,我们知道 Retrofit 主要通过注解来解释一个接口信息,这里我将 Retrofit 常用的的注解分为两大类,一种是针对方法修饰,一种是针对参数进行修饰,具体信息如下

method parameter
GET Url
POST Path
HEAD Query
PATCH QueryName
PUT QueryMap
OPTIONS Header
HTTP HeaderMap
Headers Field
Multipart FieldMap
FormUrlEncoded Part
- PartMap
- Body
- tag

知道了区别,我们来看一下 RequestFactory 的结构图以及解析的流程图。


解析流程图

可以看到解析的过程主要由两个主要方法构成,一个是 parseMethodAnnotation, 负责解析请求方法和 Header , 代码很简单就不贴了,这里我们重点看一下 parseParameterAnnotation 方法,流程图中写的很清楚,它的主要作用就是将解析的形参注解生成一个 ParameterHandler对象返回,那么这个 ParameterHandler 能做些什么呢 ? 来看看它的结构


ParameteHandler 继承关系

可以看到 ParameterHandler 的子类共有 15 个,且与上述的参数注解一一对应,其内部有一个抽象方法 apply, 两个默认方法 iterable、array、其中前者主要解析的是集合数据,后者主要针对数组的解析,但它们最终还是会调的是由各个子类实现的 apply 方法来将解析的键值对或 body 添加到我们的 request 中。

代码形式如下

abstract class ParameterHandler {
  abstract void apply(RequestBuilder builder, @Nullable T value) throws IOException;

  final ParameterHandler> iterable() {
    return new ParameterHandler>() {
      @Override void apply(RequestBuilder builder, @Nullable Iterable values)
          throws IOException {
        if (values == null) return; // Skip null values.

        for (T value : values) {
          ParameterHandler.this.apply(builder, value);
        }
      }
    };
  }

  final ParameterHandler array() {
    return new ParameterHandler() {
      @Override void apply(RequestBuilder builder, @Nullable Object values) throws IOException {
        if (values == null) return; // Skip null values.

        for (int i = 0, size = Array.getLength(values); i < size; i++) {
          //noinspection unchecked
          ParameterHandler.this.apply(builder, (T) Array.get(values, i));
        }
      }
    };
  }

 
 

其实关于解析的具体代码大家稍微耐心看看就会明白,基本过程都相同,笔者在这里就不浪费太多笔墨了。

六、Call 对象的诞生

当我们调用 API 接口时,会返回一个 call 对象来让我们进行 async/enqueue 请求,而整个 Call 对象的生成其实是一个挺复杂的过程,这里笔者会分三小节来详细展开这个过程。

6.1、HttpServiceMethod.parseAnnotations 的作用

接口的方法注解和参数注解解析完成后,我们就获得了一个 RequestFactory 对象,这个对象包含我们请求的所有信息,那么接下来看看 HttpServiceMethod.parseAnnotations 方法帮我门做了什么

abstract class HttpServiceMethod extends ServiceMethod {
  
 
static  HttpServiceMethod parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
    boolean continuationWantsResponse = false;
    boolean continuationBodyNullable = false;

    Annotation[] annotations = method.getAnnotations();
    Type adapterType;
    
    //获取要适配的 call 类型
    adapterType = method.getGenericReturnType();
   

    //创建 call 适配器,这个适配器的主要作用就是为了获取要返回的类型和要执行的回调线程
    //如果是协程则回调线程返回 null
    CallAdapter callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
    Type responseType = callAdapter.responseType();
    

    //这里就是创建转换器的地方,内部会通过上面的获取的 responseType 创建合适的转换器
    Converter responseConverter =
        createResponseConverter(retrofit, method, responseType);

    //获取 OkHttpClient 实例
    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    if (!isKotlinSuspendFunction) {
      return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    } 
  }
    
}

代码做了精简,删除了 Kotlin 相关的部分,后面会单独讲,这里先主要讲非 Kotlin 部分。

可以看到整个方法的作用就是为了返回一个 CallAdapted 对象,而创建的过程由以下步骤完成

  • method.getGenericReturnType(); 获取我们声明的返回类型 Call
  • createCallAdapter 创建一个 Callback 线程
  • createResponseConverter 创建 response 的 converter
  • 获取 Retrofit 初始化时创建的 OkHttpclient 对象
  • 组装成一个 CallAdapted 对象返回

如果光看 CallAdapted 这个名字的话是很有迷惑性的,你可能觉得会和 CallAdapter 有关系,又或者觉得这可能就是 Call 对象,这里笔者先卖个关子,后面会深入剖析,大家先记住就行。

接下来我们深入了解 CallAdapter 和 Converter 的创建过程

6.1、CallAdapter 的创建

跟进createCallAdapter 方法后会发现经过层层调用,最终会进入到 Retorift 的 nextCallAdapter 方法中进行实际的创建过程

 public CallAdapter nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
      
    int start = callAdapterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
      CallAdapter adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }
  }

这是创建的 CallAdapter 的核心方法,代码很简单就是开了个循环,遍历 callAdapterFactories 集合获取合适的 callAdapter , (如果不知道 callAdapterFactories 集合从哪来的,请看第三节),如何才叫合适呢? 很简单就是根据我们的返回类型,对应关系如下

returnType CallAdapter
Call.class DefaultCallAdapterFactory
Observable.class RxJavaCallAdapterFactory

我们目前一直使用的返回类型是 Call.class 所以上述循环中调用的其实是DefaultCallAdapterFactory 的 get 方法

 @Override public @Nullable CallAdapter get(
      Type returnType, Annotation[] annotations, Retrofit retrofit) {
    //非 Call 类型直接返回 null,遍历下一个
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    //必须为参数化类型,否则抛异常
    if (!(returnType instanceof ParameterizedType)) {
      throw new IllegalArgumentException(
          "Call return type must be parameterized as Call or Call");
    }
    final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);
    //这里其实主要的作用是为了判断是否使用了协程,如果实现了协程那么则不使用系统的回调线程并返回null
    final Executor executor = Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
        ? null
        : callbackExecutor;



    //创建一个匿名 CallAdapter 实例对象返回
    return new CallAdapter>() {
      @Override public Type responseType() {
        return responseType;
      }

      @Override public Call adapt(Call call) {
        return executor == null
            ? call
            : new ExecutorCallbackCall<>(executor, call);
      }
    };
  }
 
 

get 方法的还是很简单的就是创建了一个匿名的 CallAdapter 类并返回,这里你需要的就是重点记一下 CallAdapter的adapt 方法,因为后面会调用到,至于 callbackExecutor 内部则是通过 Handler 将我们的请求结果切换到主线程。你看下 Platform Android 环境下的 defaultCallbackExecutor 方法就明白了。至于 ExecutorCallbackCall 其实是真正执行请求的类,这里大家先了解下,后面会讲到

Retrofit 常用的 CallAdapter 整体的结构图如下


image
6.2、ResponseConverter 的创建

ResponseConverter 的调用过程可以说基本和 CallAdapter 的调用逻辑一模一样,也是层层调用,最终进入到 nextResponseBodyConverter 方法中

 public  Converter nextResponseBodyConverter(
      @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
      
    int start = converterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = converterFactories.size(); i < count; i++) {
      Converter converter =
          converterFactories.get(i).responseBodyConverter(type, annotations, this);
      if (converter != null) {
        //noinspection unchecked
        return (Converter) converter;
      }
    }
  }

和 callAdapter 的创建过程如出一辙,至于获取什么类型的 converter 其实也有一个对应关系

returnType converter
ResponseBody.class BuiltInConverters
Model.class GsonConverterFactory
Optional.class OptionalConverterFactory

显然我们的例子只有 GsonConverterFactory 符合,所以进入 responseBodyConverter 方法看看

  @Override
  public Converter responseBodyConverter(Type type, Annotation[] annotations,
      Retrofit retrofit) {
    TypeAdapter adapter = gson.getAdapter(TypeToken.get(type));
    return new GsonResponseBodyConverter<>(gson, adapter);
  }

代码很简单就是通过 Gson 帮我们获取要适配的返回类型,然后返回一个 GsonResponseBodyConverter 对象,

而 GsonResponseBodyConverter 做的事情也很简单,通过重写的 convert 方法将 RequestBody 中的流数据读取并转为我们的声明的 model 类型,
代码如下

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

所以 GsonConverterFactory.responseBodyConverter 方法的主要作用就是帮我们创建了一个 GsonResponseBodyConverter 对象来帮助我们完成 Response 的转换

至此你应该对 Retrofit 的 Convert 工作流程有了一个比较清晰的了解,其实 Retrofit 在构建的时候内部为我们提供了两个默认的 converter 分别是 BuiltInConverters 和 OptionalConverterFactory 后者只能在 Java8+ 或 Android API 24+ 环境中使用,且返回类型必须为 Optional.class 类型,感兴趣的可以自己了解下,Android 基本用不到它。而前者则是通过 I/O 流 的方式将请求的信息和响应信息包装成一个 RequestBody 和 ResponseBody ,所以当你把返回类型设置为 Call 时也可以正常请求,但返回的是最原始的字节流。虽然每个 Convert 的实现过程可能不尽相同,但基本的逻辑也是按照这个规律进行的。

Retrofit 常用的 Converter 整体的结构图如下


image
6.3、 Call 对象的创建

经过前面的分析,注解解析完了,OkHttpClient 对象有了,callAdapter 和 Converter 也创建了,至此终于满足了创建 Call 对象的条件,但只是满足了条件,此时还并未创建 Call 对象。而是将上述条件组装成了一个 CallAdapted 对象,看名字难道又是一个 callAdapter 的子类? 可是我们前面不是已经创建过了吗?要想解除疑惑,让我们看看 CallAdapted 的结构图就知道了


image

了解了 CallAdapted 的结构关系,我们进入代码看看

 static final class CallAdapted extends HttpServiceMethod {
    private final CallAdapter callAdapter;

    CallAdapted(RequestFactory requestFactory, okhttp3.Call.Factory callFactory,
        Converter responseConverter,
        CallAdapter callAdapter) {
        //将解析的注解, OkHttpClient对象,以及创建的 call 和 converter 赋值给对应的成员变量
      super(requestFactory, callFactory, responseConverter);
      this.callAdapter = callAdapter;
    }

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

可以看到 CallAdapted 作为 HttpServiceMethod 的子类,只重写了 adapt 方法,并调用了 callAdapter 的 adapt 方法并返回了一个 RetrunT 类型的对象,那么这个 RetrunT 是啥呢?

让我们回归使用的起点 ,发起一个请求

Call> call = github.contributors("square", "retrofit");

开头已经大致讲述了动态代理的基本作用,这里不再赘述。

此时整个调用过程如下,这里大家也可以跟进代码按照以下步骤跟进调用链

  • 通过动态代理的方式调用 InvocationHandler 的 invoke 方法
  • 在 invoke 方法中调用 loadServiceMethod 方法解析接口的注解,根据返回类型创建对应的 callAdapter 和 convert,组装成一个 CallAdapted 返回
  • 调用 CallAdapted 的 invoke 方法返回一个 Call 对象

前面两个步骤已经做过详细的分析,这里我们重点看下 invoke 方法。首先我们知道 CallAdapted 是 HttpServiceMethod 的子类,而 HttpServiceMethod 其实已经实现了 invoke 方法,从前面的代码中我们可以看到 CallAdapted 虽然只重写了 adapt 方法,但根据继承的特性 CallAdapted 自然也拥有了此方法。那么我们可以稍微改动一下 CallAdpated 的代码以方便大家理解

static final class CallAdapted extends HttpServiceMethod {
    private final CallAdapter callAdapter;

    @Override final @Nullable ReturnT invoke(Object[] args) {
        Call call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
        return adapt(call, args);
    }

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

可以看到在 invoke 方法中创建了一个 OkHttpCall 对象,并将我们请求的信息,参数(就是 contributors("square", "retrofit") ),OkHttpClient(就是 callFactory), 和 converter 传入。并调用了 adapt 方法, adapt 方法中又调用了 callAdapter 的 adapt 方法并将 OkHttpCall 对象传入并返回。这一系列的调用过程到底发生了什么呢?

首先看看 OkHttpCall 的类结构图


image

如图所示 OkHttpCall 的主要作用就是为了帮我们进行 async / enqueue 请求。

而 callAdapter.adapt 从前面的分析中它其实调用的是 DefaultCallAdapterFactory 的 get 方法为我们创建的一个匿名 CallAdapter 对象中实现的 adapt 方法,代码如下

final class DefaultCallAdapterFactory extends CallAdapter.Factory {
  private final @Nullable Executor callbackExecutor;
  @Override public @Nullable CallAdapter get(
      Type returnType, Annotation[] annotations, Retrofit retrofit) {
   
    //这里其实主要的作用是为了判断是否使用了协程,如果实现了协程那么则不使用系统的回调线程并返回null
    final Executor executor = Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
        ? null
        : callbackExecutor; // 内部调用 handler 切换到主线程
        
    return new CallAdapter>() {
      @Override public Type responseType() {
        return responseType;
      }

      @Override public Call adapt(Call call) {
        return executor == null
            ? call //OkHttoCall 对象
            : new ExecutorCallbackCall<>(executor, call); // 新建一个 Call 对象,并将 OkHttpCall 对象传入
      }
    };
  }
 
 

现在回过头你可能就会明白我在 callAdapter 的创建那一节为什么要让你重点关注 adapt 方法,因为 Call 对象就是在 adapt 方法中诞生的。而其中会涉及两个 Call 对象,一个是我们传入的 OkHttpCall 另一个是 ExcutorCallBackCall 对象,而具体使用哪个则源于你是否用的是协程,如果是协程则直接用 OkHttpCall 否则创建一个新的 Call 对象。

那么此时再看这行代码,相信你应该知道我们的 Call 对象是如何诞生的吧,以及用的是哪个 Call 对象了吧!

Call> call = github.contributors("square", "retrofit");

七、Call 对象的请求过程

让我们先看一段 Retrofit 标准的请求过程

Call> call = github.contributors("square", "retrofit");

call.enqueue(new Callback>() {
        @Override
        public void onResponse(Call> call, Response> response) {
            for (Contributor contributor : contributors) {
                System.out.println(contributor.login + " (" + contributor.contributions + ")");
            }
        }

        @Override
        public void onFailure(Call> call, Throwable t) {

        }
});

经过上一节的分析,我们知道了 Call 对象的诞生过程,也基本了解了 Call 的类结构,而在未使用协程的情况下我们使用的是新建的 ExecutorCallbackCall 对象,这个 ExecutorCallbackCall 也实现了 Call 接口,所以它的结构和 OkHttpCall 基本相同。让我们看看它的 enqueue 方法

static final class ExecutorCallbackCall implements Call {
    final Executor callbackExecutor;
    final Call delegate;

    ExecutorCallbackCall(Executor callbackExecutor, Call delegate) {
      this.callbackExecutor = callbackExecutor;// 主线程的 Handler 
      this.delegate = delegate; // 传入的 OkHttpCall
    }

    @Override public void enqueue(final Callback callback) {
      delegate.enqueue(new Callback() {
        @Override public void onResponse(Call call, final Response response) {
          callbackExecutor.execute(() -> {
            if (delegate.isCanceled()) {
              // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
              callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
            } else {
              callback.onResponse(ExecutorCallbackCall.this, response);
            }
          });
        }

        @Override public void onFailure(Call call, final Throwable t) {
          callbackExecutor.execute(() -> callback.onFailure(ExecutorCallbackCall.this, t));
        }
      });
    }
}

代码的逻辑并不复杂,很有意思的是 ExecutorCallbackCall 的 enqueue 方法最终又委托 OkHttpCall 的 enqueue 方法来进行真正的请求,同理 async 请求也是如此。看来无论套几层壳子,最终的活还是由 OkHttpCall 来执行啊。

OkHttpCall 将请求的 Response 返回后,在经由 callbackExecuter 将结果切换到主线程处理,这里再强调一下 callbackExecuter 对象是在 Retrofit 在构建时根据系统环境自动创建的,如果是 Android 环境下调用 defaultCallbackExecutor 方法会返回一个 MainThreadExecutor 对象,代码如下

 static final class Android extends Platform {
    Android() {
      super(Build.VERSION.SDK_INT >= 24);
    }

    @Override public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }

    static class MainThreadExecutor implements Executor {
      private final Handler handler = new Handler(Looper.getMainLooper());

      @Override public void execute(Runnable r) {
        handler.post(r);
      }
    }
  }

看来要想了解真正的请求过程,还是得进入到 OkHttpCall 中的 enqueue 中查看

@Override public void enqueue(final Callback callback) {
    okhttp3.Call call;
    Throwable failure;

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

      call = rawCall;
  
      if (call == null && failure == null) {
         call = rawCall = createRawCall();
      }
    }
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
        Response response;
       
        response = parseResponse(rawResponse);
      
        callback.onResponse(OkHttpCall.this, response);
       
      }

代码进行了精简,我们只看核心代码,其实整个 enqueue 方法总结起来干了这么几件事

  • createRawCall 创建一个 OkHttp 原生的 Call 对象
  • 通过 OkHttp 的 Call 对象发起一个 异步请求
  • parseResponse 解析返回的 response
  • callback.onResponse() 将解析的 response 回调给 ExecutorCallbackCall 的 enqueue 方法

那就简单了,接下来我们就按照这个步骤来看看,首先是 createRawCall 方法

 private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
    return call;
  }

原来是调用了 OkHttpClient 的 newCall 方法,而传入的 request 是由 requestFactory 的 create 方法提供

okhttp3.Request create(Object[] args) throws IOException {
    @SuppressWarnings("unchecked") // 参数处理对象数组
    ParameterHandler[] handlers = (ParameterHandler[]) parameterHandlers;

    int argumentCount = args.length;
    //将接口注解解析的信息放入 RequestBuilder 中
    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl,
        headers, contentType, hasBody, isFormEncoded, isMultipart);

    if (isKotlinSuspendFunction) {
      // The Continuation is the last parameter and the handlers array contains null at that index.
      argumentCount--;
    }

    //遍历参数处理对象数组,通过 Apply 方法添加进 requestBuilder 
    List argumentList = new ArrayList<>(argumentCount);
    for (int p = 0; p < argumentCount; p++) {
      argumentList.add(args[p]);
      handlers[p].apply(requestBuilder, args[p]);
    }

    //返回一个 request 对象
    return requestBuilder.get()
        .tag(Invocation.class, new Invocation(method, argumentList))
        .build();
  }
 
 

获取完 request 后,就可以直接创建一个OkHttp 的 Call 对象了,接下来就是解析 Call 对象请求后的响应了,进入 parseResponse 方法看看

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

    // 如果没有响应信息,这里创建一个没有响应体的原始 response 用于返回
    rawResponse = rawResponse.newBuilder()
        .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
        .build();

    // 省略一些判断代码

    //创建一个用于捕获在读取响应字节流过程发生 IO 异常的对象,内部通过 Okio 读取
    //该对象集成自 ResponseBody
    ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
    try {
      //读取正常,则通过 Converter 转换我们的响应,并返回
      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;
    }
  }

我们例子返回的是 Call.class 类型,所以这里的 responseConverter 对应的其实是 GsonResponseBodyConverter,而具体的转换过程也是由 GsonResponseBodyConverter 的 convert 方法处理,代码如下

final class GsonResponseBodyConverter implements Converter {
  private final Gson gson;
  private final TypeAdapter adapter;
  
   GsonResponseBodyConverter(Gson gson, TypeAdapter adapter) {
    this.gson = gson;// Gson 对象
    this.adapter = adapter;// model 的具体类型适配器
  }

  @Override public T convert(ResponseBody value) throws IOException {
    //读取 ResponseBoyd 的字符流并创建一个 JsonReader 对象
    JsonReader jsonReader = gson.newJsonReader(value.charStream());
    try {
      //通过类型适配器读取 jsonReader 并转转换为我们声明的 model 类型
      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();
    }
  }
}

解析完响应返回后,最终调用 callback.onResponse 方法回调给 ExecutorCallbackCall ,在经由 callbackExecutor 内部的 handler 将 response 回调到主线程处理。

至此对于从 Retrofit 的构建、到创建一个 API 接口,以及发起一个请求的全部过程就分析完了。也完全对应上了开头的 Retorift 流程图。纵观整个分析过程,真的感觉到设计这套框架的大佬的强大,能把各种设计模式,泛型、反射、注解灵活运用成这个样子,简直令人叹服。真——五体投地,望尘莫及。

八、结语

限于篇幅,"Retrofit 源码深入分析 —— Call 对象的诞生与请求" 就先分析到这里,如果想继续了解 Retrofit 对 RxJava 和 协程的支持,请看 Retrofit 源码深入分析 —— RxJava 和 协程的支持

你可能感兴趣的:(Retrofit 源码深入分析 —— Call 对象的诞生与请求)