Retrofit解析7之相关类解析

上篇文章讲解了Call接口、CallAdapter接口、Callback接口、Converter接口、Platform类、ExecutorCallAdapterFactory类、HttpException类。而本片文章讲解剩下的几个类。内容如下

  • 1、GsonConverterFactory类
  • 2、BuiltInConverters类
  • 3、RequestBuilder类
  • 4、Response类
  • 5、OkHttpCall类

一、GsonConverterFactory类

(一)GsonConverterFactory 简介

1、GsonConverterFactory在哪里?

有人会问GsonConverterFactory这个类在那?我怎么在Retrofit源码里面找不到,其实这个GsonConverterFactory类不在Retrofit里面,需要你在应用的时候添加的。如果很多Retrofit的程序员都是用Gson来进行反序列化的,所以他们会在他们的gradle里面添加,如下:

dependencies {
    //省略部分....
    compile 'com.squareup.retrofit2:converter-gson:2.1.0'
    //省略部分....
}

然后在代码里面添加

    //设置返回数据类型
   Retrofit.Builder.addConverterFactory(GsonConverterFactory.create()) 

这样 Retrofit就可以使用GsonConverterFactory

2、GsonConverterFactory 简介

为了更好的体验作者的意图,我这边直接把这个GsonConverterFactory类的类注释,大家一起研究下

/**
 * A {@linkplain Converter.Factory converter} which uses Gson for JSON.
 * <p>
 * Because Gson is so flexible in the types it supports, this converter assumes that it can handle
 * all types. If you are mixing JSON serialization with something else (such as protocol buffers),
 * you must {@linkplain Retrofit.Builder#addConverterFactory(Converter.Factory) add this instance}
 * last to allow the other converters a chance to see their types.
 */

我来先简单翻译一下:

使用Gson在解析JSON的一个转化器 由于Gson在兼容类型方面比较灵活,所以假设它可以处理所有类型。如果你想在其他方面(比如协议缓冲区protocol buffers)也使用该JSON转化器,为了让他其他转化器知道它们对应代理类型,则必须调用Retrofit.Builder的addConverterFactory(Converter.Factory)方法,来添加这个实例。

所以我们总结一下,就是一个JSON解析的转化器

(二)"com.squareup.retrofit2:converter-gson:2.1.0"的包结构

如下图:

包结构.png

我们发现好简单,就3个类耶,那我们就一个一个来看 先看下GsonConverterFactory

(三) 类源码解析

1、GsonConverterFactory 源码解析

这个类的类注释已经讲解过了,我就不再讲解了。直接上源码

/**
 * A {@linkplain Converter.Factory converter} which uses Gson for JSON.
 * <p>
 * Because Gson is so flexible in the types it supports, this converter assumes that it can handle
 * all types. If you are mixing JSON serialization with something else (such as protocol buffers),
 * you must {@linkplain Retrofit.Builder#addConverterFactory(Converter.Factory) add this instance}
 * last to allow the other converters a chance to see their types.
 */
public final class GsonConverterFactory extends Converter.Factory {
  /**
   * Create an instance using a default {@link Gson} instance for conversion. Encoding to JSON and
   * decoding from JSON (when no charset is specified by a header) will use UTF-8.
   */
  public static GsonConverterFactory create() {
    return create(new Gson());
  }

  /**
   * Create an instance using {@code gson} for conversion. Encoding to JSON and
   * decoding from JSON (when no charset is specified by a header) will use UTF-8.
   */
  public static GsonConverterFactory create(Gson gson) {
    return new GsonConverterFactory(gson);
  }

  private final Gson gson;

  private GsonConverterFactory(Gson gson) {
    if (gson == null) throw new NullPointerException("gson == null");
    this.gson = gson;
  }

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

  @Override
  public Converter<?, RequestBody> requestBodyConverter(Type type,
      Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new GsonRequestBodyConverter<>(gson, adapter);
  }
}

代码很少,我们来看下他的结构

GsonConverterFactory结构.png

通过代码和结构图,我们知道以下几点

  • 构造函数是private,所以不能直接new,但是他提供了两个public的静态方法来创面一个GsonConverterFactory对象。
  • GsonConverterFactory是final的不能被继承。
  • 内部有一个final的Gson类型的变量gson,在构造GsonConverterFactory被初始化
  • 如果调用GsonConverterFactory类的静态方法创建GsonConverterFactory 有两个,一个是需要传入Gson对象,一个不需要传入Gson对象,而这个不传入Gson对象的静态方法,其实内部也是自己直接new的一个Gson对象。
  • 内部定义两个public方法responseBodyConverter和requestBodyConverter
  • responseBodyConverter和requestBodyConverter方法里面均通过gson.getAdapter方法获取TypeAdapter(类型适配器)对象,这个TypeAdapter(类型适配器)对象作为构造函数的参数传入GsonResponseBodyConverter和GsonRequestBodyConverter中。

那我们来看下具体看下这两个方法内部都作了什么?

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

  @Override
  public Converter<?, RequestBody> requestBodyConverter(Type type,
      Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new GsonRequestBodyConverter<>(gson, adapter);
  }

GsonConverterFactory类定义了两个方法返回不同的Converter,一个是针对解析响应体(ResponseBody)的转化器,一个是对应请求体(RequestBody)的转化器,大家注意,他这里有进一步解耦了,因为如果是一般人,包括笔者,肯定直接就在这里直接定义具体的解析的流程,而GsonConverterFactory没有,他是直接定义两个类,针对两个不同的情况。这样,解析响应体(ResponseBody)和解析请求体(RequestBody)就进一步解耦了。所以,我们知道GsonResponseBodyConverter是负责转化响应体(ResponseBody)的,而GsonRequestBodyConverter是负责转化请求体(RequestBody)的

2、GsonResponseBodyConverter 源码解析

这个类比较简单,直接上源码了

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 {
      return adapter.read(jsonReader);
    } finally {
      value.close();
    }
  }
}

比较简单,直接说结论了

  • GsonResponseBodyConverter是final的不能被继承
  • 内部有两个变量分别是Gson类型的gson和TypeAdapter<T>的adapter,两者是在构造函数里面初始化的。
  • 实现Converter<ResponseBody, T>,所以实现了T convert(ResponseBody) 方法,内部是通过gson的newJsonReader的方法获取JsonReader对象,然后用TypeAdapter的read来获取对应的类型T
3、GsonRequestBodyConverter 源码解析

这个类比较简单,直接上源码了

final class GsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
  private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");
  private static final Charset UTF_8 = Charset.forName("UTF-8");

  private final Gson gson;
  private final TypeAdapter<T> adapter;

  GsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
    this.gson = gson;
    this.adapter = adapter;
  }

  @Override public RequestBody convert(T value) throws IOException {
    Buffer buffer = new Buffer();
    Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
    JsonWriter jsonWriter = gson.newJsonWriter(writer);
    adapter.write(jsonWriter, value);
    jsonWriter.close();
    return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
  }
}

比较简单,直接说结论了,内容如下:

  • GsonRequestBodyConverter是final的不能被继承
  • 内部有两个变量分别是Gson类型的gson和TypeAdapter<T>的adapter,两者是在构造函数里面初始化的。
  • 字符支持是 UTF-8的
  • 实现 converter<T, RequestBody>,所以实现了RequestBody convert(T value) 方法,首先获取输出流,然后通过gson.newJsonWriter()方法获取JsonWriter对象,最后通过 adapter.write()写入数据。最后调用RequestBody.create()方法获取一个RequestBody。

PS:

关于Gson的相关知识,后面有时间再仔细讲解下,今天就不说了。

至此,GsonConverterFactory已经讲解完毕,希望他对能能理解。

二、BuiltInConverters

BuiltInConverters类Retrofit内部自带的转化器,也就是默认的转化器。受篇幅限制,我就不上源码了,希望大家理解

通过源码我们知道如下内容

  • BuiltInConverters是final的不允许继承
  • BuiltInConverters里面就两个方法,即重写了responseBodyConverter和requestBodyConverter两个方法,它并重写stringConverter这个方法。
  • 它内部有5个静态内部类。那我们就挨个分析
1、responseBodyConverter方法分析

主要是把响应体转化为指定的格式,其实就是反序列化的过程,内容不多,大家看下源码:

  @Override
  public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
      Retrofit retrofit) {
      //如果type类型是ResponseBody
    if (type == ResponseBody.class) {
      //判断是二进制流的形式,还是字符的形式,返回不同的对象,如果是二进制流的形式,返回静态内部类StreamingResponseBodyConverter的单例,如果是字符,则返回静态内部类BufferingResponseBodyConverter的单例。
      return Utils.isAnnotationPresent(annotations, Streaming.class)
          ? StreamingResponseBodyConverter.INSTANCE
          : BufferingResponseBodyConverter.INSTANCE;
    }
     //如果是Void则说明不关心响应体,所以由静态内部类VoidResponseBodyConverter的单例来处理。
    if (type == Void.class) {
      return VoidResponseBodyConverter.INSTANCE;
    }
    //由于是默认的转化器,只处理ResponseBody和Void的类型
    return null;
  }

内容不多,我就不讲解,大家看注释吧

2、requestBodyConverter方法分析

这个方法主要是把对象写入到请求体重,内容不多,直接上源码,受篇幅限制,直接上注释,就不解释了。

  @Override
  public Converter<?, RequestBody> requestBodyConverter(Type type,
      Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
    //如果type的原始接口和RequestBody一致,则调用静态内部类RequestBodyConverter的实例
    if (RequestBody.class.isAssignableFrom(Utils.getRawType(type))) {
      return RequestBodyConverter.INSTANCE;
    }
     //默认只支持RequestBody的类型
    return null;
  }
3、静态内部类分析RequestBodyConverter

这个类很简单, convert()方法直接返回value

  static final class RequestBodyConverter implements Converter<RequestBody, RequestBody> {
    static final RequestBodyConverter INSTANCE = new RequestBodyConverter();

    @Override public RequestBody convert(RequestBody value) throws IOException {
      return value;
    }
  }
4、静态内部类分析StreamingResponseBodyConverter

这个类很简单, convert()方法直接返回value

  static final class StreamingResponseBodyConverter
      implements Converter<ResponseBody, ResponseBody> {
    static final StreamingResponseBodyConverter INSTANCE = new StreamingResponseBodyConverter();

    @Override public ResponseBody convert(ResponseBody value) throws IOException {
      return value;
    }
  }
5、静态内部类分析BufferingResponseBodyConverter

这个类很简单, convert()方法直接返回value

  static final class BufferingResponseBodyConverter
      implements Converter<ResponseBody, ResponseBody> {
    static final BufferingResponseBodyConverter INSTANCE = new BufferingResponseBodyConverter();

    @Override public ResponseBody convert(ResponseBody value) throws IOException {
      try {
        // Buffer the entire body to avoid future I/O.
         //通过调用Utils的buff()方法来获取一个ResponseBody对象,其实Utils的buff()内部调用ResponseBody.create()来获取一个ResponseBody
        return Utils.buffer(value);
      } finally {
        value.close();
      }
    }
  }
6、静态内部类分析BufferingResponseBodyConverter

convert()方法起就是调用对象自己的toString()方法。

  static final class ToStringConverter implements Converter<Object, String> {
    static final ToStringConverter INSTANCE = new ToStringConverter();

    @Override public String convert(Object value) {
      return value.toString();
    }
  }

总结

  • BuiltInConverters根据不同的类型,来把具体转化交给具体的静态内部类去处理,实现了解耦。
  • 大部分的转化,其实都没有转化,都是直接取的值。

三、RequestBuilder类

通过字面大家就能猜到了他是一个Request的builder类。这个Request是okhttp3.Request。

(一) 看源码

(1)、大体上浏览源码

老规矩,先上源码

final class RequestBuilder {
  private static final char[] HEX_DIGITS =
      { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
  private static final String PATH_SEGMENT_ALWAYS_ENCODE_SET = " \"<>^`{}|\\?#";

  private final String method;

  private final HttpUrl baseUrl;
  private String relativeUrl;
  private HttpUrl.Builder urlBuilder;

  private final Request.Builder requestBuilder;
  private MediaType contentType;

  private final boolean hasBody;
  private MultipartBody.Builder multipartBuilder;
  private FormBody.Builder formBuilder;
  private RequestBody body;

  RequestBuilder(String method, HttpUrl baseUrl, String relativeUrl, Headers headers,
      MediaType contentType, boolean hasBody, boolean isFormEncoded, boolean isMultipart) {
    this.method = method;
    this.baseUrl = baseUrl;
    this.relativeUrl = relativeUrl;
    this.requestBuilder = new Request.Builder();
    this.contentType = contentType;
    this.hasBody = hasBody;

    if (headers != null) {
      requestBuilder.headers(headers);
    }

    if (isFormEncoded) {
      // Will be set to 'body' in 'build'.
      formBuilder = new FormBody.Builder();
    } else if (isMultipart) {
      // Will be set to 'body' in 'build'.
      multipartBuilder = new MultipartBody.Builder();
      multipartBuilder.setType(MultipartBody.FORM);
    }
  }

  void setRelativeUrl(Object relativeUrl) {
    if (relativeUrl == null) throw new NullPointerException("@Url parameter is null.");
    this.relativeUrl = relativeUrl.toString();
  }

  void addHeader(String name, String value) {
    if ("Content-Type".equalsIgnoreCase(name)) {
      MediaType type = MediaType.parse(value);
      if (type == null) {
        throw new IllegalArgumentException("Malformed content type: " + value);
      }
      contentType = type;
    } else {
      requestBuilder.addHeader(name, value);
    }
  }

  void addPathParam(String name, String value, boolean encoded) {
    if (relativeUrl == null) {
      // The relative URL is cleared when the first query parameter is set.
      throw new AssertionError();
    }
    relativeUrl = relativeUrl.replace("{" + name + "}", canonicalizeForPath(value, encoded));
  }

  private static String canonicalizeForPath(String input, boolean alreadyEncoded) {
    int codePoint;
    for (int i = 0, limit = input.length(); i < limit; i += Character.charCount(codePoint)) {
      codePoint = input.codePointAt(i);
      if (codePoint < 0x20 || codePoint >= 0x7f
          || PATH_SEGMENT_ALWAYS_ENCODE_SET.indexOf(codePoint) != -1
          || (!alreadyEncoded && (codePoint == '/' || codePoint == '%'))) {
        // Slow path: the character at i requires encoding!
        Buffer out = new Buffer();
        out.writeUtf8(input, 0, i);
        canonicalizeForPath(out, input, i, limit, alreadyEncoded);
        return out.readUtf8();
      }
    }

    // Fast path: no characters required encoding.
    return input;
  }

  private static void canonicalizeForPath(Buffer out, String input, int pos, int limit,
      boolean alreadyEncoded) {
    Buffer utf8Buffer = null; // Lazily allocated.
    int codePoint;
    for (int i = pos; i < limit; i += Character.charCount(codePoint)) {
      codePoint = input.codePointAt(i);
      if (alreadyEncoded
          && (codePoint == '\t' || codePoint == '\n' || codePoint == '\f' || codePoint == '\r')) {
        // Skip this character.
      } else if (codePoint < 0x20 || codePoint >= 0x7f
          || PATH_SEGMENT_ALWAYS_ENCODE_SET.indexOf(codePoint) != -1
          || (!alreadyEncoded && (codePoint == '/' || codePoint == '%'))) {
        // Percent encode this character.
        if (utf8Buffer == null) {
          utf8Buffer = new Buffer();
        }
        utf8Buffer.writeUtf8CodePoint(codePoint);
        while (!utf8Buffer.exhausted()) {
          int b = utf8Buffer.readByte() & 0xff;
          out.writeByte('%');
          out.writeByte(HEX_DIGITS[(b >> 4) & 0xf]);
          out.writeByte(HEX_DIGITS[b & 0xf]);
        }
      } else {
        // This character doesn't need encoding. Just copy it over.
        out.writeUtf8CodePoint(codePoint);
      }
    }
  }

  void addQueryParam(String name, String value, boolean encoded) {
    if (relativeUrl != null) {
      // Do a one-time combination of the built relative URL and the base URL.
      urlBuilder = baseUrl.newBuilder(relativeUrl);
      if (urlBuilder == null) {
        throw new IllegalArgumentException(
            "Malformed URL. Base: " + baseUrl + ", Relative: " + relativeUrl);
      }
      relativeUrl = null;
    }

    if (encoded) {
      urlBuilder.addEncodedQueryParameter(name, value);
    } else {
      urlBuilder.addQueryParameter(name, value);
    }
  }

  void addFormField(String name, String value, boolean encoded) {
    if (encoded) {
      formBuilder.addEncoded(name, value);
    } else {
      formBuilder.add(name, value);
    }
  }

  void addPart(Headers headers, RequestBody body) {
    multipartBuilder.addPart(headers, body);
  }

  void addPart(MultipartBody.Part part) {
    multipartBuilder.addPart(part);
  }

  void setBody(RequestBody body) {
    this.body = body;
  }

  Request build() {
    HttpUrl url;
    HttpUrl.Builder urlBuilder = this.urlBuilder;
    if (urlBuilder != null) {
      url = urlBuilder.build();
    } else {
      // No query parameters triggered builder creation, just combine the relative URL and base URL.
      url = baseUrl.resolve(relativeUrl);
      if (url == null) {
        throw new IllegalArgumentException(
            "Malformed URL. Base: " + baseUrl + ", Relative: " + relativeUrl);
      }
    }

    RequestBody body = this.body;
    if (body == null) {
      // Try to pull from one of the builders.
      if (formBuilder != null) {
        body = formBuilder.build();
      } else if (multipartBuilder != null) {
        body = multipartBuilder.build();
      } else if (hasBody) {
        // Body is absent, make an empty body.
        body = RequestBody.create(null, new byte[0]);
      }
    }

    MediaType contentType = this.contentType;
    if (contentType != null) {
      if (body != null) {
        body = new ContentTypeOverridingRequestBody(body, contentType);
      } else {
        requestBuilder.addHeader("Content-Type", contentType.toString());
      }
    }

    return requestBuilder
        .url(url)
        .method(method, body)
        .build();
  }

  private static class ContentTypeOverridingRequestBody extends RequestBody {
    private final RequestBody delegate;
    private final MediaType contentType;

    ContentTypeOverridingRequestBody(RequestBody delegate, MediaType contentType) {
      this.delegate = delegate;
      this.contentType = contentType;
    }

    @Override public MediaType contentType() {
      return contentType;
    }

    @Override public long contentLength() throws IOException {
      return delegate.contentLength();
    }

    @Override public void writeTo(BufferedSink sink) throws IOException {
      delegate.writeTo(sink);
    }
  }
}

简单的源码,我们可以轻易得出以下结论

  • 这个类既不是public的也不是private的,所以只能包内引用。
  • 这个类是final,所以没有子类
  • 这个有一个静态内部类ContentTypeOverridingRequestBody
  • RequestBuilder只有一个构造函数,既不是public,也不是private,没有无参的构造函数。
  • 有两个常量char[] HEX_DIGITS和String PATH_SEGMENT_ALWAYS_ENCODE_SET
(2)、变量分析
  //请求类型,post或者get
  private final String method;
   //Http的url  根地址
  private final HttpUrl baseUrl;
   //相对路径的url,对应的是具体的接口
  private String relativeUrl;
   //HttpUrl的Builder
  private HttpUrl.Builder urlBuilder;
  //okHttp3里面的Request.Builder
  private final Request.Builder requestBuilder;
  // MediaType 也是okHttp3里面的MediaTyep。MediaType即是Internet Media Type,互联网媒体类型;也叫做MIME类型,在http协议消息头中,使用Content-Type来表示具体请求中的媒体类型信息。
  private MediaType contentType;
  // 布尔类型,代表都是有body
  private final boolean hasBody;
   //okHttp3里面的MultipartBody的builder
  private MultipartBody.Builder multipartBuilder;
  //okHttp3里面的FormBody的builder
  private FormBody.Builder formBuilder;
  //okHttp3里面的RequestBody
  private RequestBody body;

我基本上每个变量都给注释了。下面我来研究下构造函数

(2)、构造函数分析

看下代码

RequestBuilder(String method, HttpUrl baseUrl, String relativeUrl, Headers headers,
      MediaType contentType, boolean hasBody, boolean isFormEncoded, boolean isMultipart) {
    this.method = method;
    this.baseUrl = baseUrl;
    this.relativeUrl = relativeUrl;
    this.requestBuilder = new Request.Builder();
    this.contentType = contentType;
    this.hasBody = hasBody;

    if (headers != null) {
      requestBuilder.headers(headers);
    }

    if (isFormEncoded) {
      // Will be set to 'body' in 'build'.
      formBuilder = new FormBody.Builder();
    } else if (isMultipart) {
      // Will be set to 'body' in 'build'.
      multipartBuilder = new MultipartBody.Builder();
      multipartBuilder.setType(MultipartBody.FORM);
    }
  }

构造函数很简单,就是字段赋值,最后判断是 表单 提交还是 文件 上传,如果是文件上传,则设置类型。

(3)、相关的set方法分析

里面有什么类似set的方法,我们这里一次性说下

  void setRelativeUrl(Object relativeUrl) {
    if (relativeUrl == null) throw new NullPointerException("@Url parameter is null.");
    this.relativeUrl = relativeUrl.toString();
  }
  void addHeader(String name, String value) {
    if ("Content-Type".equalsIgnoreCase(name)) {
      MediaType type = MediaType.parse(value);
      if (type == null) {
        throw new IllegalArgumentException("Malformed content type: " + value);
      }
      contentType = type;
    } else {
      requestBuilder.addHeader(name, value);
    }
  }

  void addPathParam(String name, String value, boolean encoded) {
    if (relativeUrl == null) {
      // The relative URL is cleared when the first query parameter is set.
      throw new AssertionError();
    }
    relativeUrl = relativeUrl.replace("{" + name + "}", canonicalizeForPath(value, encoded));
  }

void addQueryParam(String name, String value, boolean encoded) {
    if (relativeUrl != null) {
      // Do a one-time combination of the built relative URL and the base URL.
      urlBuilder = baseUrl.newBuilder(relativeUrl);
      if (urlBuilder == null) {
        throw new IllegalArgumentException(
            "Malformed URL. Base: " + baseUrl + ", Relative: " + relativeUrl);
      }
      relativeUrl = null;
    }

    if (encoded) {
      urlBuilder.addEncodedQueryParameter(name, value);
    } else {
      urlBuilder.addQueryParameter(name, value);
    }
  }

  void addFormField(String name, String value, boolean encoded) {
    if (encoded) {
      formBuilder.addEncoded(name, value);
    } else {
      formBuilder.add(name, value);
    }
  }

  void addPart(Headers headers, RequestBody body) {
    multipartBuilder.addPart(headers, body);
  }

  void addPart(MultipartBody.Part part) {
    multipartBuilder.addPart(part);
  }

  void setBody(RequestBody body) {
    this.body = body;
  }

相关方法分析

  • setRelativeUrl:设置相对路径的url
  • addHeader:添加请求头
  • addPathParam:添加PathParam参数
  • addQueryParam:添加请求参数,如果是GET方法,直接拼接在相对路径上,如果是post方式则直接放到消息体,这里面其实通过okHttp3的HttpUrl.Builder来实现的。
  • addFormField:添加表单,key-value键值对的形式
  • addPart:这个方法有两个重载,一个是根据header和body来添加MultipartBody,另外一个是直接添加MultipartBody
  • setBody:设置请求的body
(3)、build()方法分析

build()主要就是生成一个用于okHttp请求的request,一起看下源码

Request build() {
    HttpUrl url;
    HttpUrl.Builder urlBuilder = this.urlBuilder;
    //初始化url
    if (urlBuilder != null) {
      url = urlBuilder.build();
    } else {
      // No query parameters triggered builder creation, just combine the relative URL and base URL.
      url = baseUrl.resolve(relativeUrl);
      if (url == null) {
        throw new IllegalArgumentException(
            "Malformed URL. Base: " + baseUrl + ", Relative: " + relativeUrl);
      }
    }

    RequestBody body = this.body;
    //如果没有body
    if (body == null) {
      // Try to pull from one of the builders.
      if (formBuilder != null) {
        body = formBuilder.build();
      } else if (multipartBuilder != null) {
        body = multipartBuilder.build();
      } else if (hasBody) {
        // Body is absent, make an empty body.
        body = RequestBody.create(null, new byte[0]);
      }
    }
    //设置类型
    MediaType contentType = this.contentType;
    if (contentType != null) {
      if (body != null) {
        body = new ContentTypeOverridingRequestBody(body, contentType);
      } else {
        requestBuilder.addHeader("Content-Type", contentType.toString());
      }
    }
    //返回request
    return requestBuilder
        .url(url)
        .method(method, body)
        .build();
  }
(4)、静态内部类ContentTypeOverridingRequestBody分析

代码很少,直接看源码

  private static class ContentTypeOverridingRequestBody extends RequestBody {
    private final RequestBody delegate;
    private final MediaType contentType;

    ContentTypeOverridingRequestBody(RequestBody delegate, MediaType contentType) {
      this.delegate = delegate;
      this.contentType = contentType;
    }

    @Override public MediaType contentType() {
      return contentType;
    }

    @Override public long contentLength() throws IOException {
      return delegate.contentLength();
    }

    @Override public void writeTo(BufferedSink sink) throws IOException {
      delegate.writeTo(sink);
    }
  }

看源码得出如下结论

  • ContentTypeOverridingRequestBody是RequestBody的实现类。
  • ContentTypeOverridingRequestBody其实是一个代理类,真正的body是delegate。

PS:还有两个静态方法是给Patch请求参数,规范化的,没什么好讲的。

(二) 总结

RequestBuilder这个类,代码很简单,主要就是用于创建一个okHttp的Request,里面有很多方法用于设置Request的一些参数。

四、Response类

这个类很简单,代码也才100多行,看名字,就是会知道是一个"响应"。老规矩,看下源码:

(一)、源码分析

/** An HTTP response. */
public final class Response<T> {
  /** Create a synthetic successful response with {@code body} as the deserialized body. */
  public static <T> Response<T> success(T body) {
    return success(body, new okhttp3.Response.Builder() //
        .code(200)
        .message("OK")
        .protocol(Protocol.HTTP_1_1)
        .request(new Request.Builder().url("http://localhost/").build())
        .build());
  }

  /**
   * Create a synthetic successful response using {@code headers} with {@code body} as the
   * deserialized body.
   */
  public static <T> Response<T> success(T body, Headers headers) {
    if (headers == null) throw new NullPointerException("headers == null");
    return success(body, new okhttp3.Response.Builder() //
        .code(200)
        .message("OK")
        .protocol(Protocol.HTTP_1_1)
        .headers(headers)
        .request(new Request.Builder().url("http://localhost/").build())
        .build());
  }

  /**
   * Create a successful response from {@code rawResponse} with {@code body} as the deserialized
   * body.
   */
  public static <T> Response<T> success(T body, okhttp3.Response rawResponse) {
    if (rawResponse == null) throw new NullPointerException("rawResponse == null");
    if (!rawResponse.isSuccessful()) {
      throw new IllegalArgumentException("rawResponse must be successful response");
    }
    return new Response<>(rawResponse, body, null);
  }

  /**
   * Create a synthetic error response with an HTTP status code of {@code code} and {@code body}
   * as the error body.
   */
  public static <T> Response<T> error(int code, ResponseBody body) {
    if (code < 400) throw new IllegalArgumentException("code < 400: " + code);
    return error(body, new okhttp3.Response.Builder() //
        .code(code)
        .protocol(Protocol.HTTP_1_1)
        .request(new Request.Builder().url("http://localhost/").build())
        .build());
  }

  /** Create an error response from {@code rawResponse} with {@code body} as the error body. */
  public static <T> Response<T> error(ResponseBody body, okhttp3.Response rawResponse) {
    if (body == null) throw new NullPointerException("body == null");
    if (rawResponse == null) throw new NullPointerException("rawResponse == null");
    if (rawResponse.isSuccessful()) {
      throw new IllegalArgumentException("rawResponse should not be successful response");
    }
    return new Response<>(rawResponse, null, body);
  }

  private final okhttp3.Response rawResponse;
  private final T body;
  private final ResponseBody errorBody;

  private Response(okhttp3.Response rawResponse, T body, ResponseBody errorBody) {
    this.rawResponse = rawResponse;
    this.body = body;
    this.errorBody = errorBody;
  }

  /** The raw response from the HTTP client. */
  public okhttp3.Response raw() {
    return rawResponse;
  }

  /** HTTP status code. */
  public int code() {
    return rawResponse.code();
  }

  /** HTTP status message or null if unknown. */
  public String message() {
    return rawResponse.message();
  }

  /** HTTP headers. */
  public Headers headers() {
    return rawResponse.headers();
  }

  /** Returns true if {@link #code()} is in the range [200..300). */
  public boolean isSuccessful() {
    return rawResponse.isSuccessful();
  }

  /** The deserialized response body of a {@linkplain #isSuccessful() successful} response. */
  public T body() {
    return body;
  }

  /** The raw response body of an {@linkplain #isSuccessful() unsuccessful} response. */
  public ResponseBody errorBody() {
    return errorBody;
  }

  @Override public String toString() {
    return rawResponse.toString();
  }
}

其实通过类的注释,大家就知道这是一个HTTP的响应。通过阅读这个类的源码,我们可以获取如下信息:

  • Response类是final,无子类。
  • Response类就一个构造函数,还是private的。
  • Response类含有三个成员变量,均是是在构造的时候赋值的。
  • Response类其中的一个成员变量是
  • 有一些关于响应的get方法,但是其实均是调用rawResponse的自己对应的方法rawResponse,它的类型是okhttp3.Response。
  • 有5个静态函数用于获取一个Response的实例

(二)、如何获取Response的实例

由于Response的构造方法是private,所以要想获取Response必须通过他的5个静态方法。那我们来以来了解下这5个静态方法

1、success(T body, okhttp3.Response rawResponse)方法分析

为什么先看这个方法,因为其他两个成功静态方法最后都调用了这个方法,那么我们来先看下源码

  /**
   * Create a successful response from {@code rawResponse} with {@code body} as the deserialized
   * body.
   */
  public static <T> Response<T> success(T body, okhttp3.Response rawResponse) {
    if (rawResponse == null) throw new NullPointerException("rawResponse == null");
    if (!rawResponse.isSuccessful()) {
      throw new IllegalArgumentException("rawResponse must be successful response");
    }
    return new Response<>(rawResponse, body, null);
  }

先看一下方法注释:

通过rawResponse的body作为反序列化的输入来创建一个成功的Retrofit的响应Response。

PS:这里的rawResponse的类型是okhttp3.Response,这个类里面一共有两个Response,一个是Retrofit的Response一个是okhttp3.Response。大家千万别弄混了。

这个方法内容很简单,首先判断rawResponse不能为null,否则抛异常,然后直接由把传进来的两个参数body和rawResponse作为参数,构造一个Retrofit的Response。而第三个参数代表的是 "错误" 的消息体,这个事成功的响应,所以这里第三个参数是null。

2、public static <T> Response<T> success(T body) 方法分析

先看源码

  /** Create a synthetic successful response with {@code body} as the deserialized body. */
  public static <T> Response<T> success(T body) {
    return success(body, new okhttp3.Response.Builder() //
        .code(200)
        .message("OK")
        .protocol(Protocol.HTTP_1_1)
        .request(new Request.Builder().url("http://localhost/").build())
        .build());
  }

先简单翻一下方法的注释: 通过使用响应体(body)的反序列化来 "合成" 一个 "成功的" 响应(response)。首先 new okhttp3.Response.Builder(),然后设置相关参数,最后在调用.build()产生一个okhttp3.Response,作为入参,调入自己两个入参的sucess静态方法。

3、Response<T> success(T body, Headers headers)
  /**
   * Create a synthetic successful response using {@code headers} with {@code body} as the
   * deserialized body.
   */
  public static <T> Response<T> success(T body, Headers headers) {
    if (headers == null) throw new NullPointerException("headers == null");
    return success(body, new okhttp3.Response.Builder() //
        .code(200)
        .message("OK")
        .protocol(Protocol.HTTP_1_1)
        .headers(headers)
        .request(new Request.Builder().url("http://localhost/").build())
        .build());
  }

先看下方法的注释:

通过使用body和headers作为输入来创建一个合成的成功的response响应。

它和上面的区别就是这个方法多了一个入参headers,并且在okhttp3.Response.Builder的headers()set进去的。

4、Response<T> error(ResponseBody body, okhttp3.Response rawResponse)
  /** Create an error response from {@code rawResponse} with {@code body} as the error body. */
  public static <T> Response<T> error(ResponseBody body, okhttp3.Response rawResponse) {
    if (body == null) throw new NullPointerException("body == null");
    if (rawResponse == null) throw new NullPointerException("rawResponse == null");
    if (rawResponse.isSuccessful()) {
      throw new IllegalArgumentException("rawResponse should not be successful response");
    }
    return new Response<>(rawResponse, null, body);
  }

翻译一下注释:

通过使用body和rawResponse作为输入,来创建一个错误的响应。

首先,做了非空检查;然后是错误检查,因为是错误的响应,所以rawResponse.isSuccessful()是false;最后new Response返回,new Response的入参是rawResponse,null,body。

5、Response<T> error(int code, ResponseBody body)

看下源码

/**
   * Create a synthetic error response with an HTTP status code of {@code code} and {@code body}
   * as the error body.
   */
  public static <T> Response<T> error(int code, ResponseBody body) {
    if (code < 400) throw new IllegalArgumentException("code < 400: " + code);
    return error(body, new okhttp3.Response.Builder() //
        .code(code)
        .protocol(Protocol.HTTP_1_1)
        .request(new Request.Builder().url("http://localhost/").build())
        .build());
  }

先看下类注释:

通过使用code和body作为输入,来创建一个合成的错误的Response(响应体)。

首先new了一个okhttp3.Response.Builder,然后设置一些属性, 然后把这个okhttp3.Response.Builder作为参数,调用上面的error方法。

(三)、总结

Response是Retrofit的内"设定"的的响应,由于Response的构造方式是private的,所以创建则是由success和error五个静态方法来获取Response,但是Response的一些方法其实本质是调用的是rawResponse的一些方法。

五 OkHttpCall

(一)、OkHttpCall是什么?

从类名选择上,我的第一反应是okHttp的call的包装类,但是实际是的确是这样的,ok大家怀着这样的一个问题一起来阅读下源码:

(二) 源码阅读:

final class OkHttpCall<T> implements Call<T> {
  private final ServiceMethod<T, ?> serviceMethod;
  private final Object[] args;

  private volatile boolean canceled;

  // All guarded by this.
  private okhttp3.Call rawCall;
  private Throwable creationFailure; // Either a RuntimeException or IOException.
  private boolean executed;

  OkHttpCall(ServiceMethod<T, ?> serviceMethod, Object[] args) {
    this.serviceMethod = serviceMethod;
    this.args = args;
  }

  @SuppressWarnings("CloneDoesntCallSuperClone") // We are a final type & this saves clearing state.
  @Override public OkHttpCall<T> clone() {
    return new OkHttpCall<>(serviceMethod, args);
  }

  @Override 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);
    }
  }

  @Override public void enqueue(final Callback<T> callback) {
    if (callback == null) throw new NullPointerException("callback == null");

    okhttp3.Call call;
    Throwable failure;

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

      call = rawCall;
      failure = creationFailure;
      if (call == null && failure == null) {
        try {
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          failure = creationFailure = t;
        }
      }
    }

    if (failure != null) {
      callback.onFailure(this, failure);
      return;
    }

    if (canceled) {
      call.cancel();
    }

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

  @Override public synchronized boolean isExecuted() {
    return executed;
  }

  @Override public Response<T> execute() throws IOException {
    okhttp3.Call call;

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

      if (creationFailure != null) {
        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());
  }

  private okhttp3.Call createRawCall() throws IOException {
    Request request = serviceMethod.toRequest(args);
    okhttp3.Call call = serviceMethod.callFactory.newCall(request);
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }

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

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

  public void cancel() {
    canceled = true;

    okhttp3.Call call;
    synchronized (this) {
      call = rawCall;
    }
    if (call != null) {
      call.cancel();
    }
  }

  @Override public boolean isCanceled() {
    if (canceled) {
      return true;
    }
    synchronized (this) {
      return rawCall != null && rawCall.isCanceled();
    }
  }

  static final class NoContentResponseBody extends ResponseBody {
    private final MediaType contentType;
    private final long contentLength;

    NoContentResponseBody(MediaType contentType, long contentLength) {
      this.contentType = contentType;
      this.contentLength = contentLength;
    }

    @Override public MediaType contentType() {
      return contentType;
    }

    @Override public long contentLength() {
      return contentLength;
    }

    @Override public BufferedSource source() {
      throw new IllegalStateException("Cannot read raw response body of a converted body.");
    }
  }

  static final class ExceptionCatchingRequestBody extends ResponseBody {
    private final ResponseBody delegate;
    IOException thrownException;

    ExceptionCatchingRequestBody(ResponseBody delegate) {
      this.delegate = delegate;
    }

    @Override public MediaType contentType() {
      return delegate.contentType();
    }

    @Override public long contentLength() {
      return delegate.contentLength();
    }

    @Override public BufferedSource source() {
      return Okio.buffer(new ForwardingSource(delegate.source()) {
        @Override public long read(Buffer sink, long byteCount) throws IOException {
          try {
            return super.read(sink, byteCount);
          } catch (IOException e) {
            thrownException = e;
            throw e;
          }
        }
      });
    }

    @Override public void close() {
      delegate.close();
    }

    void throwIfCaught() throws IOException {
      if (thrownException != null) {
        throw thrownException;
      }
    }
  }
}

大家先粗略的看下,后续再一个接着一个去慢慢讲解,读完源码有什么感觉?

1、源码初读

我读源码可以获取如下信息

  • OkHttpCall<T>是final的,是不允许继承的
  • OkHttpCall<T>是实现的retrofit2.Call<T>接口
  • 有一个两个参数的构造函数,但是不是public,意味着只能包内掉用,外部无法调用。
  • 有两个静态内部类NoContentResponseBody和ExceptionCatchingRequestBody
  • 有个private的成员变量canceled是** "volatile" **的,估计会有多线程对其操作

初步阅读源码我能得到这么多的信息,那我们再来仔细的阅读下源码

2、分析成员变量

OkHttpCall<T> 成员变量很少,我直接在源码上注释了

  //方法处理类,后面详解讲解,大家这里有个印象即可
  private final ServiceMethod<T, ?> serviceMethod;
  //和方法处理类一样,其实是调用方法的入参,大家先知道就可以,后期会详细讲解。
  private final Object[] args;
  // 判断 是否已经取消了。
  //volatile,如果有同学不清楚,请直接百度下,这个关键字还是很重要的,主要用于多线程使用的背景下。
  private volatile boolean canceled;
  // All guarded by this.
  //真正的okhttp3.Call,真正的请求
  private okhttp3.Call rawCall;
  // 异常,包括IO和运行时异常
  private Throwable creationFailure; // Either a RuntimeException or IOException.
  //是否已经启动
  private boolean executed;
3、分析构造函数
  OkHttpCall(ServiceMethod<T, ?> serviceMethod, Object[] args) {
    this.serviceMethod = serviceMethod;
    this.args = args;
  }

构造函数很简单,需要传入一个ServiceMethod和args,然后进行赋值,如果大家对泛型认识不是很熟练,这里是可以知道ServiceMethod<T, ?>里面的第一个泛型的类型和OkHttpCall<T>是同一个类型即可。

4、静态内部类分析之NoContentResponseBody

看类名,我们就知道是一个空的响应体,那我们看下源码

  static final class NoContentResponseBody extends ResponseBody {
    private final MediaType contentType;
    private final long contentLength;

    NoContentResponseBody(MediaType contentType, long contentLength) {
      this.contentType = contentType;
      this.contentLength = contentLength;
    }

    @Override public MediaType contentType() {
      return contentType;
    }

    @Override public long contentLength() {
      return contentLength;
    }

    @Override public BufferedSource source() {
      throw new IllegalStateException("Cannot read raw response body of a converted body.");
    }
  }

通过源码我们可知

  • 这个NoContentResponseBody类是final的,不允许继承。
  • 这个NoContentResponseBody类是ResponseBody的子类。
  • 这个类有两个属性contentType,contentLength,且均是final,构造时候赋值之后,就不能变更了
  • 这个类不能读取,读取就会报错

NoContentResponseBody这个类比较简单,就是没有body内容,但是有contentType和contentLength,这时候我们就像那body去哪里了?难道body被"吃"了,ok这里留个一悬念,大家把这个问题,记住,后面我会一一解答的。

5、静态内部类分析之ExceptionCatchingRequestBody

通过类名,我们大概可以分析好像是一个请求体异常捕获的类了,是不是这样?我们还是看一下源码

  static final class ExceptionCatchingRequestBody extends ResponseBody {
    private final ResponseBody delegate;
    IOException thrownException;

    ExceptionCatchingRequestBody(ResponseBody delegate) {
      this.delegate = delegate;
    }

    @Override public MediaType contentType() {
      return delegate.contentType();
    }

    @Override public long contentLength() {
      return delegate.contentLength();
    }

    @Override public BufferedSource source() {
      return Okio.buffer(new ForwardingSource(delegate.source()) {
        @Override public long read(Buffer sink, long byteCount) throws IOException {
          try {
            return super.read(sink, byteCount);
          } catch (IOException e) {
            thrownException = e;
            throw e;
          }
        }
      });
    }

    @Override public void close() {
      delegate.close();
    }

    void throwIfCaught() throws IOException {
      if (thrownException != null) {
        throw thrownException;
      }
    }
  }

通过读取源码我们可以获取如下信息:

  • ExceptionCatchingRequestBody同样是final的不允许继承
  • ExceptionCatchingRequestBody是继承ResponseBody
  • ExceptionCatchingRequestBody有个属性是okhttp3.ResponseBody 类型的,不是retrofit2.Response.且这个属性是final的,在构造函数里面被赋值。
  • thrownException这个属性在构造的时候没有被赋值,那什么时候被复制的那? 其实在source()方法里面有个try{}catch捕获异常,当出现异常的时候就把异常赋值给thrownException。
  • 无论是contentType()还是contentLength()或是close(),其实内部调用是delegate对应的方法。所以可以看出ExceptionCatchingRequestBody其实也是一个包括类。
6、具体的方法分析
6.1、clone()方法

我们知道这个是实现retrofit2.Call的抽象方法,主要用于克隆一个call

  @SuppressWarnings("CloneDoesntCallSuperClone") // We are a final type & this saves clearing state.
  @Override public OkHttpCall<T> clone() {
    return new OkHttpCall<>(serviceMethod, args);
  }

这个clone()方法很简单,就是同样的参数直接new了一个OkHttpCall对象

6.3、request()方法
  @Override public synchronized Request request() {
    okhttp3.Call call = rawCall;
    //先判断rawCall是否为null,如果不为null,之前new过,则直接返回
    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;
      }
    }
     //通过createRawCall()来创建一个新的okhttp3.Call call
    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);
    }
  }

大体流程如下:

  • 1、判断rawCall是否已经存在,如果存在直接调用rawCall的request()
  • 2、如果rawCall不为空,再判断是不是因为之前创建的时候出现过异常导致rawCall不能被创建,所以做了异常判断,如果有异常说明之前也是创建过,只不过创建失败了,直接抛出异常。
  • 3、如果rawCall为null,并且没有异常,则肯定是首次创建,然后调用createRawCall()创建一个okhttp3.Call 。

下面就让我们了解下createRawCall()方法

6.4、createRawCall()方法
  private okhttp3.Call createRawCall() throws IOException {
    Request request = serviceMethod.toRequest(args);
    okhttp3.Call call = serviceMethod.callFactory.newCall(request);
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }

大家发现是通过ServiceMethod类的toRequest方法,入参是args,就可以获取一个Request了。然后调用ServiceMethod类的属性callFactory的newCall方法传入一个request就可以获取一个 okhttp3.Cal。那ServiceMothod是什么?大家别着急我们马上就要讲解这个类了

6.5、execute() 方法

我们知道这个是实现retrofit2.Call的抽象方法,主要用于发起同步请求

  @Override public Response<T> execute() throws IOException {
    okhttp3.Call call;

    synchronized (this) {
      //如果已经启动了/执行了,就不能重复启动/执行
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;
      //如果有异常,则说明之前出现异常,直接抛出异常
      if (creationFailure != null) {
        if (creationFailure instanceof IOException) {
          throw (IOException) creationFailure;
        } else {
          throw (RuntimeException) creationFailure;
        }
      }
     
      call = rawCall;
      //如果rawCall为null,则通过createRawCall()方法来获取一个okhttp3.Call对象。
      if (call == null) {
        try {
          call = rawCall = createRawCall();
        } catch (IOException | RuntimeException e) {
          creationFailure = e;
          throw e;
        }
      }
    }
    //如果请求被取消了,则取消请求
    if (canceled) {
      call.cancel();
    }
    //先调用call.execute(),这里补充下call.execute()返回的是okhttp3.Response,然后调用parseResponse()方法。
    return parseResponse(call.execute());
  }

大体流程如下:

  • 1、先判断是否应启动/执行了,如果已经启动/执行,就不能重复启动/执行,直接抛异常
  • 2、判断是否之前出现过异常,如果出现异常则直接抛出
  • 3、判断rawCall是否为null,如果null则调用createRawCall()获取一个okhttp3.Call。
  • 4、判断请求是否已经被取消了,如果已经被取消了,则取消。
  • 5、执行call.execute()方法
  • 6、把all.execute()执行的返回值okhttp3.Response作为入参,再调用parseResponse()方法
6.5、parseResponse() 方法

这个方法,看名字就知道是一个解析Response的方法。就是将请求结果反序列化,内部调用ServiceMethod中的responseConverter,也就是配置里的反序列化工具生成JavaBean。

 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.
     //创建一个新的okhttp3.Response,且body是null,但是还是有大小和类型
    rawResponse = rawResponse.newBuilder()
        .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
        .build();
     //获取HTTP的状态码
    int code = rawResponse.code();
     //如果不是成功的HTTP状态码
    if (code < 200 || code >= 300) {
      try {
        // Buffer the entire body to avoid future I/O.
        //获取对应的响应体内容
        ResponseBody bufferedBody = Utils.buffer(rawBody);
        //调用Response的静态方法error来获取一个retrofit2.Response
        return Response.error(bufferedBody, rawResponse);
      } finally {
        rawBody.close();
      }
    }
     //如果HTTP状态码是204或者205则说明没有响应体或者重置内容
    if (code == 204 || code == 205) {
      rawBody.close();
      //调用Response的静态方法success来获取一个retrofit2.Response
      return Response.success(null, rawResponse);
    }
    //new了一个ExceptionCatchingRequestBody对象
    ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
    try {
       //调用serviceMethod的toResponse()的方法可以返回一个T类型的body
      T body = serviceMethod.toResponse(catchingBody);
       //调用Response的静态方法success来获取一个retrofit2.Response
      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、先获取响应体,存在一个变量rawBody里面
  • 2、通过okhttp3.Response. newBuilder和NoContentResponseBody对象,重新构建一个新的okhttp3.Response给rawResponse
  • 3、获取HTTP状态码
  • 4、处理异常状态码,通过Response.error()方法获取一个Response返回
  • 5、处理状态码204和205状态
  • 6、创建一个ExceptionCatchingRequestBody对象
  • 7、调用serviceMethod.toResponse()方法获取一个类型为T的body对象。
  • 8、把类型为T的body作为入参调用Response.success(body, rawResponse)获取一个对应的retrofit2.Response,并返回。

大家还记得之前的那个问题吗?NoContentResponseBody里面的body为什么没有内容,其实没有被"吃",而是被提前取出,这样就实现了HTTP 状态码和内容的解耦,厉害啊老铁。

6.6、enqueue(final Callback<T> callback) 方法

看名字我会认为是一个异步请求的方法,具体看下代码

 @Override public void enqueue(final Callback<T> callback) {
    if (callback == null) throw new NullPointerException("callback == null");

    okhttp3.Call call;
    Throwable failure;
  
    synchronized (this) {
       //检查是否启动/执行过
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;

      call = rawCall;
      failure = creationFailure;
      //判断是否产生过错误
      if (call == null && failure == null) {
        try {
          //创建call
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          failure = creationFailure = t;
        }
      }
    }

    if (failure != null) {
      callback.onFailure(this, failure);
      return;
    }
    //判断是否取消
    if (canceled) {
      call.cancel();
    }
    //加入okhttp的请求队列
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
          throws IOException {
        Response<T> response;
        try {
          //调用parse方法解析response,进行反序列化,获取对应的JavaBean
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          callFailure(e);
          return;
        }
        调用callSuccess()方法
        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();
        }
      }
    });
  }

大体流程如下:

  • 1、检查是否执行过
  • 2、创建rawCall
  • 3、检查是否产生过错误
  • 4、检查是否已经取消
  • 5、将raw加入队列
  • 6、处理回调
6.7 其他方法

其他方法比较简单,直接在简单说下

  • isCanceled():判断是否已经被取消
  • cancel():取消请求
  • isExecuted():是否已经执行

(三) 总结:

OkHttpCall仅仅是一个包装类,外部使用Retrofit.Call,其实内部调用的是Okhttp3.Call,由OkHttpCall来实现调度。所以对外部来说,无需关心** 网络层 **倒是怎么操作的。

至此整个OkHttpCall已经介绍完毕了,大家有没有对这个类有更清晰的了解,当担大家也有一些疑惑,比如ServiceMethod的关系,大家别着急,我们现在就来怼一下Retrofit最关键的类之一ServiceMethod,请关注下一篇文章。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏微信公众号:Java团长

自己手写一个SpringMVC框架

前端框架很多,但没有一个框架称霸,后端框架现在Spring已经完成大一统。所以学习Spring是Java程序员的必修课。

1022
来自专栏IT笔记

Grafana+Prometheus系统监控之SpringBoot

前言 前一段时间使用SpringBoot创建了一个webhook项目,由于近期项目中也使用了不少SpringBoot相关的项目,趁着周末,配置一下使用prome...

1.5K7
来自专栏Java成神之路

Spring_总结_04_高级配置(二)之条件注解@Conditional

在上一节,我们了解到 Profile 为不同环境下使用不同的配置提供了支持,那么Profile到底是如何实现的呢?其实Profile正是通过条件注解来实现的。

933
来自专栏菩提树下的杨过

java调用.net asmx / wcf

一、先用asmx与wcf写二个.net web service: 1.1 asmx web服务:asmx-service.asmx.cs 1 using Sy...

2325
来自专栏猿天地

高性能NIO框架Netty-对象传输

上篇文章高性能NIO框架Netty入门篇我们对Netty做了一个简单的介绍,并且写了一个入门的Demo,客户端往服务端发送一个字符串的消息,服务端回复一个字符串...

3118
来自专栏Golang语言社区

Golang Stub初体验

序言 对于领域对象的UT测试来说,基础设施层(infra)的操作函数都应该被打桩。对于Golang来说,大家通常会想到GoMock。GoMock是由Golang...

4189
来自专栏cmazxiaoma的架构师之路

SpringBoot之路(二)之Web进阶

3604
来自专栏一个会写诗的程序员的博客

《Springboot极简教程》 Springboot plus Kotlin :Hello,WorldKotlin, Console: Hello,WorldSpringBoot Kotlin JP

https://github.com/MiniSpringBootTutorial/mini_springboot/blob/master/src/main/k...

734
来自专栏闻道于事

从源码看Spring Boot 2.0.1

Spring Boot 命名配置很少,却可以做到和其他配置复杂的框架相同的功能工作,从源码来看是怎么做到的。

902
来自专栏SpringBoot 核心技术

第三十七章:基于SpringBoot架构以及参数装载完成接口安全认证

47710

扫码关注云+社区

领取腾讯云代金券