前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Retrofit解析7之相关类解析

Retrofit解析7之相关类解析

作者头像
隔壁老李头
发布2018-08-30 11:49:37
2.9K0
发布2018-08-30 11:49:37
举报
文章被收录于专栏:Android 研究Android 研究

上篇文章讲解了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里面添加,如下:

代码语言:javascript
复制
dependencies {
    //省略部分....
    compile 'com.squareup.retrofit2:converter-gson:2.1.0'
    //省略部分....
}

然后在代码里面添加

代码语言:javascript
复制
    //设置返回数据类型
   Retrofit.Builder.addConverterFactory(GsonConverterFactory.create()) 

这样 Retrofit就可以使用GsonConverterFactory

2、GsonConverterFactory 简介

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

代码语言:javascript
复制
/**
 * 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 源码解析

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

代码语言:javascript
复制
/**
 * 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中。

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

代码语言:javascript
复制
  @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 源码解析

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

代码语言:javascript
复制
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 源码解析

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

代码语言:javascript
复制
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方法分析

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

代码语言:javascript
复制
  @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方法分析

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

代码语言:javascript
复制
  @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

代码语言:javascript
复制
  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

代码语言:javascript
复制
  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

代码语言:javascript
复制
  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()方法。

代码语言:javascript
复制
  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)、大体上浏览源码

老规矩,先上源码

代码语言:javascript
复制
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)、变量分析
代码语言:javascript
复制
  //请求类型,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)、构造函数分析

看下代码

代码语言:javascript
复制
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的方法,我们这里一次性说下

代码语言:javascript
复制
  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,一起看下源码

代码语言:javascript
复制
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分析

代码很少,直接看源码

代码语言:javascript
复制
  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多行,看名字,就是会知道是一个"响应"。老规矩,看下源码:

(一)、源码分析
代码语言:javascript
复制
/** 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)方法分析

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

代码语言:javascript
复制
  /**
   * 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) 方法分析

先看源码

代码语言:javascript
复制
  /** 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)
代码语言:javascript
复制
  /**
   * 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)
代码语言:javascript
复制
  /** 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)

看下源码

代码语言:javascript
复制
/**
   * 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大家怀着这样的一个问题一起来阅读下源码:

(二) 源码阅读:
代码语言:javascript
复制
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> 成员变量很少,我直接在源码上注释了

代码语言:javascript
复制
  //方法处理类,后面详解讲解,大家这里有个印象即可
  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、分析构造函数
代码语言:javascript
复制
  OkHttpCall(ServiceMethod<T, ?> serviceMethod, Object[] args) {
    this.serviceMethod = serviceMethod;
    this.args = args;
  }

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

4、静态内部类分析之NoContentResponseBody

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

代码语言:javascript
复制
  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

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

代码语言:javascript
复制
  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

代码语言:javascript
复制
  @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()方法
代码语言:javascript
复制
  @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()方法
代码语言:javascript
复制
  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的抽象方法,主要用于发起同步请求

代码语言:javascript
复制
  @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。

代码语言:javascript
复制
 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) 方法

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

代码语言:javascript
复制
 @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,请关注下一篇文章。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017.06.27 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、GsonConverterFactory类
    • (一)GsonConverterFactory 简介
      • (二)"com.squareup.retrofit2:converter-gson:2.1.0"的包结构
        • (三) 类源码解析
        • 二、BuiltInConverters
          • 总结
          • 三、RequestBuilder类
            • (一) 看源码
              • (二) 总结
                • (一)、源码分析
                • (二)、如何获取Response的实例
                • PS:这里的rawResponse的类型是okhttp3.Response,这个类里面一共有两个Response,一个是Retrofit的Response一个是okhttp3.Response。大家千万别弄混了。
                • (三)、总结
                • (一)、OkHttpCall是什么?
                • (二) 源码阅读:
                • (三) 总结:
            • 四、Response类
            • 五 OkHttpCall
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档