Retrofit 源码阅读笔记(二)

Retrofit 源码阅读笔记(二)

第一篇文章中介绍了 Retrofit 的动态代理实现、方法注解解析等等内容:Retrofit 源码阅读笔记(一)

本篇文章是系列文章的第二篇。

方法参数解析

在第一篇文章中介绍了 Http 请求的解析是通过 RequestFactory.Builder#build() 方法完成,在它的内部又分为两步来完成解析,通过 parseMethodAnnotation() 方法完成方法注解的解析(该方法已经在前面的文章中介绍了),通过 parseParameter() 来完成对方法参数的解析,方法参数的解析就是本节的内容。

首先我们来看看 RequestFactory.Builder#parseParameter() 方法的源码:

Java 复制代码
    private @Nullable ParameterHandler<?> parseParameter(
        int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) {
      ParameterHandler<?> result = null;
      if (annotations != null) {
        // 遍历当前参数的注解
        for (Annotation annotation : annotations) {
          // 解析注解,如果 annotationAction 不为空,就表示当前注解解析成功
          ParameterHandler<?> annotationAction =
              parseParameterAnnotation(p, parameterType, annotations, annotation);
          // 失败就继续查找
          if (annotationAction == null) {
            continue;
          }
          // 表示有重复的注解,报错。
          if (result != null) {
            throw parameterError(
                method, p, "Multiple Retrofit annotations found, only one allowed.");
          }

          result = annotationAction;
        }
      }
      // 如果没有找到 Retrofit 的注解
      if (result == null) {
        // 这里表示是最后一个参数。
        if (allowContinuation) {
          try {
            if (Utils.getRawType(parameterType) == Continuation.class) {
              // 这里表示是 Kotlin 协程的 suspend 函数
              isKotlinSuspendFunction = true;
              return null;
            }
          } catch (NoClassDefFoundError ignored) {
          }
        }
        // 非 suspend 的 Continuation.class 参数没有注解,直接报错。
        throw parameterError(method, p, "No Retrofit annotation found.");
      }

      return result;
    }

在请求参数中必须有 Retrofit 的参数注解修饰,而且只能有一个。有一种参数可以例外,那就是 Kotlin 协程中的 suspend 函数的最后一个参数可以不用注解修饰,那个参数固定为 Continuation 类型。你又和 Retrofit 学会了如何判断 suspend 函数😂。

我们继续看看 parseParameterAnnotation() 函数的实现,由于它的实现代码比较长,我就分不同的注解来分析:

@Url

@Url 注解表示该参数是请求的完整地址,他和方法注解中的 Path 会冲突。

Java 复制代码
    @Nullable
    private ParameterHandler<?> parseParameterAnnotation(
        int p, Type type, Annotation[] annotations, Annotation annotation) {
      if (annotation instanceof Url) {
        // 校验类型
        validateResolvableType(p, type);
        // 如果已经获取到 Url 报错。
        if (gotUrl) {
          throw parameterError(method, p, "Multiple @Url method annotations found.");
        }
        // 如果已经有 @Path 注解,报错
        if (gotPath) {
          throw parameterError(method, p, "@Path parameters may not be used with @Url.");
        }
        
        // 如果 @Query 注解在 @Url 注解前报错
        if (gotQuery) {
          throw parameterError(method, p, "A @Url parameter must not come after a @Query.");
        }
        // 如果 @QueryName 注解在 @Url 注解前报错
        if (gotQueryName) {
          throw parameterError(method, p, "A @Url parameter must not come after a @QueryName.");
        }
        // 如果 @QueryMap 注解在 @Url 注解前报错
        if (gotQueryMap) {
          throw parameterError(method, p, "A @Url parameter must not come after a @QueryMap.");
        }
        // 如果在方法的注解中已经获取到了 Path,报错。
        if (relativeUrl != null) {
          throw parameterError(method, p, "@Url cannot be used with @%s URL", httpMethod);
        }

        gotUrl = true;
        // 参数类型只支持以下类型
        if (type == HttpUrl.class
            || type == String.class
            || type == URI.class
            || (type instanceof Class && "android.net.Uri".equals(((Class<?>) type).getName()))) {
          return new ParameterHandler.RelativeUrl(method, p);
        } else {
          // 类型不支持报错
          throw parameterError(
              method,
              p,
              "@Url must be okhttp3.HttpUrl, String, java.net.URI, or android.net.Uri type.");
        }

      }
      // ...
      
   }

这里对 @Url 注解做一个总结:

  1. 不能使用多个 @Url 注解。
  2. @Path 注解冲突。
  3. @Query@QueryName@QueryMap 只能在 @Url 之后。
  4. 与方法注解中的相对地址冲突。
  5. 参数的类型只能是 HttpUrl(OkHttp 中的类)、StringURIUri(Android 中的类)其中之一。
  6. 参数注解处理类是 ParameterHandler.RelativeUrl

我们再来看看 ParameterHandler.RelativeUrl 源码:

Java 复制代码
  static final class RelativeUrl extends ParameterHandler<Object> {
    private final Method method;
    private final int p;

    RelativeUrl(Method method, int p) {
      this.method = method;
      this.p = p;
    }
    
    // apply 函数就是处理函数
    @Override
    void apply(RequestBuilder builder, @Nullable Object value) {
      if (value == null) {
        throw Utils.parameterError(method, p, "@Url parameter is null.");
      }
      // 直接把 @Url 的参数设置到 RequestBuilder 中
      builder.setRelativeUrl(value);
    }
  }

构建 RequestBuilder 的处理方法就是 apply() 方法,在 RelativeUrl 中直接就是把参数值通过 RequestBuilder#setRelativeUrl() 方法设置进去。

@Path

@Path 就是前面一篇文章中提到的相对路径占位符。我这里举一个例子:

Java 复制代码
@POST("/a/{name}/c")  
Call<String> foo(@Path("name") String path);

在执行时 Path 中的 {name} 就会被替换成 path 参数中的值。

我们来看看 @Path 注解处理的代码:

Java 复制代码
    @Nullable
    private ParameterHandler<?> parseParameterAnnotation(
        int p, Type type, Annotation[] annotations, Annotation annotation) {
      // ...
      if (annotation instanceof Path) {
        validateResolvableType(p, type);
        // 在 @Query 之后报错
        if (gotQuery) {
          throw parameterError(method, p, "A @Path parameter must not come after a @Query.");
        }
        // 在 @QueryName 之后报错
        if (gotQueryName) {
          throw parameterError(method, p, "A @Path parameter must not come after a @QueryName.");
        }
        // 在 @QueryMap 之后报错
        if (gotQueryMap) {
          throw parameterError(method, p, "A @Path parameter must not come after a @QueryMap.");
        }
        // 与 @Url 冲突
        if (gotUrl) {
          throw parameterError(method, p, "@Path parameters may not be used with @Url.");
        }
        // 在方法注解中必须已经设置相对路径
        if (relativeUrl == null) {
          throw parameterError(
              method, p, "@Path can only be used with relative url on @%s", httpMethod);
        }
        gotPath = true;

        Path path = (Path) annotation;
        // 获取相对路径占位符的名字
        String name = path.value();
        // 校验占位符的名字是否存在
        validatePathName(p, name);
        
        // 获取当前参数类型转换成 String 类型的转换器。
        Converter<?, String> converter = retrofit.stringConverter(type, annotations);
        return new ParameterHandler.Path<>(method, p, name, converter, path.encoded());

      }
      // ...
      
   }

@Path 做一个总结:

  1. 定义必须在 @Query@QueryName@QueryMap 等注解之前。
  2. @Url 注解冲突
  3. 在方法注解中必须定义相对路径
  4. @Path 注解中的名字必须在相对路径的占位符中存在
  5. 获取参数类型转换成 String 类型的 Converter,这个就是一个类型转换器,很多地方都用它做类型转换,我们也可以自定义相关的 ConverterFactory 来完成我们自己需要的类型转换,开发中常见的就是 JsonRequestBodyRequestBodyJson,我们后面会单独分析 ConverterFactory
  6. 参数处理类是 ParameterHandler.Path

看看 ParameterHandler.Path 的源码:

Java 复制代码
  static final class Path<T> extends ParameterHandler<T> {
    private final Method method;
    private final int p;
    private final String name;
    private final Converter<T, String> valueConverter;
    private final boolean encoded;

    Path(Method method, int p, String name, Converter<T, String> valueConverter, boolean encoded) {
      this.method = method;
      this.p = p;
      this.name = Objects.requireNonNull(name, "name == null");
      this.valueConverter = valueConverter;
      this.encoded = encoded;
    }

    @Override
    void apply(RequestBuilder builder, @Nullable T value) throws IOException {
      if (value == null) {
        throw Utils.parameterError(
            method, p, "Path parameter \"" + name + "\" value must not be null.");
      }
      // 直接将原来的占位符,替换成真实的值。
      builder.addPathParam(name, valueConverter.convert(value), encoded);
    }
  }

直接通过 RequestBuilder#addPathParam() 方法将相对路径的参数替换成真实的值,参数的值通过 Converter#convert() 方法转换成 String

@Query

@Query 注解就不用多说了,就是 Http 协议中的 Query 参数,这里注意 @QueryKey 是可以重复的,如果重复的话就当成是一个数组处理。

Java 复制代码
    @Nullable
    private ParameterHandler<?> parseParameterAnnotation(
        int p, Type type, Annotation[] annotations, Annotation annotation) {
      // ...
      if (annotation instanceof Query) {
        validateResolvableType(p, type);
        Query query = (Query) annotation;
        // 从注解中获取参数名字
        String name = query.value();
        boolean encoded = query.encoded();
        
        // 获取参数类型
        Class<?> rawParameterType = Utils.getRawType(type);
        gotQuery = true;
        if (Iterable.class.isAssignableFrom(rawParameterType)) {
          // 如果是迭代器类型
          if (!(type instanceof ParameterizedType)) {
            throw parameterError(
                method,
                p,
                rawParameterType.getSimpleName()
                    + " must include generic type (e.g., "
                    + rawParameterType.getSimpleName()
                    + "<String>)");
          }
          ParameterizedType parameterizedType = (ParameterizedType) type;
          // 获取迭代器泛型类型
          Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
          // 获取转换成 String 的 Converter
          Converter<?, String> converter = retrofit.stringConverter(iterableType, annotations);
          return new ParameterHandler.Query<>(name, converter, encoded).iterable();
        } else if (rawParameterType.isArray()) {
          // 如果是数组,处理方式和迭代器类似
          Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
          Converter<?, String> converter =
              retrofit.stringConverter(arrayComponentType, annotations);
          return new ParameterHandler.Query<>(name, converter, encoded).array();
        } else {
          // 普通类型,处理方式更加简单。
          Converter<?, String> converter = retrofit.stringConverter(type, annotations);
          return new ParameterHandler.Query<>(name, converter, encoded);
      }
      // ...
      
   }

@Query 做一个总结:

  1. @Query 可以处理数组或者迭代器类型的参数,还可以处理普通类型的参数。
  2. 参数最终需要转换成 String,如果是迭代器或者数组,是他们的 Element 需要转换成 String
  3. 普通类型的参数处理类是 ParameterHandler.Query,如果是迭代器和数组会调用它的 iterable()array() 方法转换。

看看 ParameterHandler.Query 的源码:

Java 复制代码
   static final class Query<T> extends ParameterHandler<T> {
    private final String name;
    private final Converter<T, String> valueConverter;
    private final boolean encoded;

    Query(String name, Converter<T, String> valueConverter, boolean encoded) {
      this.name = Objects.requireNonNull(name, "name == null");
      this.valueConverter = valueConverter;
      this.encoded = encoded;
    }

    @Override
    void apply(RequestBuilder builder, @Nullable T value) throws IOException {
      // 参数为空跳过
      if (value == null) return; // Skip null values.
      // 转换成 String
      String queryValue = valueConverter.convert(value);
      // 空跳过
      if (queryValue == null) return; // Skip converted but null values
      // 将 Query参数添加到 RequestBuilder 中
      builder.addQueryParam(name, queryValue, encoded);
    }
  }

首先将参数通过 Converter 转换成 String,通过 RequestBuilder#addQueryParam() 方法添加到 Request 中。

@QueryName

它和 @Query 差不多,只是它的 value 值是空的,就只是指定可以空的 Query。它的处理代码也几乎和 @Query 一样的,只是它的参数处理类换成了 ParameterHandler.QueryName,我们直接看看它的源代码:

Java 复制代码
   static final class QueryName<T> extends ParameterHandler<T> {
    private final Converter<T, String> nameConverter;
    private final boolean encoded;

    QueryName(Converter<T, String> nameConverter, boolean encoded) {
      this.nameConverter = nameConverter;
      this.encoded = encoded;
    }

    @Override
    void apply(RequestBuilder builder, @Nullable T value) throws IOException {
      if (value == null) return; // Skip null values.
      // Value 值直接设置为空
      builder.addQueryParam(nameConverter.convert(value), null, encoded);
    }
  }

基本可以说和 Query 一模一样,只是 value 值设置为空。

@QueryMap

相对于 @Query 注解只能指定单个 Query,而 @QueryMap 就可以指定多个 Query,它的参数类型必须是 Map

Java 复制代码
    @Nullable
    private ParameterHandler<?> parseParameterAnnotation(
        int p, Type type, Annotation[] annotations, Annotation annotation) {
      // ...
      if (annotation instanceof QueryMap) {
        validateResolvableType(p, type);
        Class<?> rawParameterType = Utils.getRawType(type);
        gotQueryMap = true;
        // 参数类型必须是 Map 类型
        if (!Map.class.isAssignableFrom(rawParameterType)) {
          throw parameterError(method, p, "@QueryMap parameter type must be Map.");
        }
        Type mapType = Utils.getSupertype(type, rawParameterType, Map.class);
        if (!(mapType instanceof ParameterizedType)) {
          throw parameterError(
              method, p, "Map must include generic types (e.g., Map<String, String>)");
        }
        ParameterizedType parameterizedType = (ParameterizedType) mapType;
        // Map 的 Key 的类型
        Type keyType = Utils.getParameterUpperBound(0, parameterizedType);
        // key 的类型必须是 String
        if (String.class != keyType) {
          throw parameterError(method, p, "@QueryMap keys must be of type String: " + keyType);
        }
        Type valueType = Utils.getParameterUpperBound(1, parameterizedType);
        // 通过 value 类型找到转换成 String 类型的 Converter。
        Converter<?, String> valueConverter = retrofit.stringConverter(valueType, annotations);

        return new ParameterHandler.QueryMap<>(
            method, p, valueConverter, ((QueryMap) annotation).encoded());

      }
      // ...
      
   }
   

简单总结一下:

  1. 参数类型必须是 MapMapKey 的类型必须是 String
  2. 需要将 MapValue 转换成 String
  3. 最后的参数处理类是 ParameterHandler.QueryMap

再来看看 ParameterHandler.QueryMap 的源码:

Java 复制代码
  static final class QueryMap<T> extends ParameterHandler<Map<String, T>> {
    private final Method method;
    private final int p;
    private final Converter<T, String> valueConverter;
    private final boolean encoded;

    QueryMap(Method method, int p, Converter<T, String> valueConverter, boolean encoded) {
      this.method = method;
      this.p = p;
      this.valueConverter = valueConverter;
      this.encoded = encoded;
    }

    @Override
    void apply(RequestBuilder builder, @Nullable Map<String, T> value) throws IOException {
      if (value == null) {
        throw Utils.parameterError(method, p, "Query map was null");
      }
      // 遍历 Map
      for (Map.Entry<String, T> entry : value.entrySet()) {
        String entryKey = entry.getKey();
        // Key 不能为空
        if (entryKey == null) {
          throw Utils.parameterError(method, p, "Query map contained null key.");
        }
        T entryValue = entry.getValue();
        if (entryValue == null) {
          throw Utils.parameterError(
              method, p, "Query map contained null value for key '" + entryKey + "'.");
        }
        // 将 Value 转换成 String
        String convertedEntryValue = valueConverter.convert(entryValue);
        // Value 不能为空
        if (convertedEntryValue == null) {
          throw Utils.parameterError(
              method,
              p,
              "Query map value '"
                  + entryValue
                  + "' converted to null by "
                  + valueConverter.getClass().getName()
                  + " for key '"
                  + entryKey
                  + "'.");
        }
        // 添加 Query
        builder.addQueryParam(entryKey, convertedEntryValue, encoded);
      }
    }
  }

参数的 KeyValue 都不能为空,其他的就和 @Query 类似了。

@Header

用来描述 Http 请求的 Header,在源码中的处理方式和 @Query 注解一模一样,就不看它的源码了,看看它的参数处理类 ParameterHandler.Header 源码:

Java 复制代码
   Header(String name, Converter<T, String> valueConverter) {
      this.name = Objects.requireNonNull(name, "name == null");
      this.valueConverter = valueConverter;
    }

    @Override
    void apply(RequestBuilder builder, @Nullable T value) throws IOException {
      if (value == null) return; // Skip null values.

      String headerValue = valueConverter.convert(value);
      if (headerValue == null) return; // Skip converted but null values.

      builder.addHeader(name, headerValue);
    }
  }

上面的代码也是朴实无华,直接调用 ReqeustBuilder#addHeader() 方法添加 HttpHeader

@HeaderMap

@HeaderMap@Header 的关系,就相当于 @QueryMap@Query 的关系,它的处理方式和 @QueryMap 一样,源码就不看了,这里看它的参数处理类 ParameterHandler.HeaderMap 的源码实现:

Java 复制代码
  static final class HeaderMap<T> extends ParameterHandler<Map<String, T>> {
    private final Method method;
    private final int p;
    private final Converter<T, String> valueConverter;

    HeaderMap(Method method, int p, Converter<T, String> valueConverter) {
      this.method = method;
      this.p = p;
      this.valueConverter = valueConverter;
    }

    @Override
    void apply(RequestBuilder builder, @Nullable Map<String, T> value) throws IOException {
      if (value == null) {
        throw Utils.parameterError(method, p, "Header map was null.");
      }

      for (Map.Entry<String, T> entry : value.entrySet()) {
        String headerName = entry.getKey();
        if (headerName == null) {
          throw Utils.parameterError(method, p, "Header map contained null key.");
        }
        T headerValue = entry.getValue();
        if (headerValue == null) {
          throw Utils.parameterError(
              method, p, "Header map contained null value for key '" + headerName + "'.");
        }
        builder.addHeader(headerName, valueConverter.convert(headerValue));
      }
    }
  }

也没有什么特别之处,相对于 @Header 的处理,@HeaderMap 会遍历它的所有 Key-Value 然后分别调用 RequestBuilder#addHeader() 方法。

@Field

仅限方法被 @FormUrlEncoded 修饰的方法,表示表单类型的 Body 的参数。

Java 复制代码
    @Nullable
    private ParameterHandler<?> parseParameterAnnotation(
        int p, Type type, Annotation[] annotations, Annotation annotation) {
      // ...
      if (annotation instanceof Field) {
        validateResolvableType(p, type);
        // 方法没有被 @FormUrlEncoded 报错
        if (!isFormEncoded) {
          throw parameterError(method, p, "@Field parameters can only be used with form encoding.");
        }
        Field field = (Field) annotation;
        // 获取参数名
        String name = field.value();
        boolean encoded = field.encoded();

        gotField = true;

        Class<?> rawParameterType = Utils.getRawType(type);
        if (Iterable.class.isAssignableFrom(rawParameterType)) {
          // 如果是迭代器类型的参数
          if (!(type instanceof ParameterizedType)) {
            throw parameterError(
                method,
                p,
                rawParameterType.getSimpleName()
                    + " must include generic type (e.g., "
                    + rawParameterType.getSimpleName()
                    + "<String>)");
          }
          ParameterizedType parameterizedType = (ParameterizedType) type;
          // 获取迭代器泛型的参数类型
          Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
          // 获取迭代器类型转 String 的 Converter。
          Converter<?, String> converter = retrofit.stringConverter(iterableType, annotations);
          return new ParameterHandler.Field<>(name, converter, encoded).iterable();
        } else if (rawParameterType.isArray()) {
          // 数组类型处理
          Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
          Converter<?, String> converter =
              retrofit.stringConverter(arrayComponentType, annotations);
          return new ParameterHandler.Field<>(name, converter, encoded).array();
        } else {
          // 除数组,迭代器的其他类型
          Converter<?, String> converter = retrofit.stringConverter(type, annotations);
          return new ParameterHandler.Field<>(name, converter, encoded);
        }
      // ...
      
   }

简单总结(和 @Query 也比较类似):

  1. 方法必须有 @FormUrlEncoded 修饰。
  2. 处理迭代器类型和数组类型,生成的参数处理器通过 Parameterhandelr.Fielditerable()array() 方法处理后生成的处理器,他们的 Converter 也都是迭代器或者数组的泛型类型转 String
  3. 除了数组和迭代器的其他的普通类型,直接用当前类型转 StringConverter,使用的参数处理器是 Parameterhandelr.Field

继续看看 Parameterhandelr.Field 的源码实现:

Java 复制代码
   static final class Field<T> extends ParameterHandler<T> {
    private final String name;
    private final Converter<T, String> valueConverter;
    private final boolean encoded;

    Field(String name, Converter<T, String> valueConverter, boolean encoded) {
      this.name = Objects.requireNonNull(name, "name == null");
      this.valueConverter = valueConverter;
      this.encoded = encoded;
    }

    @Override
    void apply(RequestBuilder builder, @Nullable T value) throws IOException {
      if (value == null) return; // Skip null values.
     
      String fieldValue = valueConverter.convert(value);
      // 跳过空的数据
      if (fieldValue == null) return; // Skip null converted values
      // 将 Field 添加到参数中
      builder.addFormField(name, fieldValue, encoded);
    }
  }

跳过空的数据,通过 RequestBuilder#addFormField() 将表单 Feild 参数添加到 Request 中。

@FeildMap

@FeildMap@Feild 的关系,就相当于 @QueryMap@Query 的关系,处理逻辑和 @QueryMap 类似(只是有 @FormUrlEncoded 限制),源代码就贴了,它的参数处理类是 ParameterHandler.FieldMap,看看它的源码:

Java 复制代码
static final class FieldMap<T> extends ParameterHandler<Map<String, T>> {
    private final Method method;
    private final int p;
    private final Converter<T, String> valueConverter;
    private final boolean encoded;

    FieldMap(Method method, int p, Converter<T, String> valueConverter, boolean encoded) {
      this.method = method;
      this.p = p;
      this.valueConverter = valueConverter;
      this.encoded = encoded;
    }

    @Override
    void apply(RequestBuilder builder, @Nullable Map<String, T> value) throws IOException {
      if (value == null) {
        throw Utils.parameterError(method, p, "Field map was null.");
      }
      
      for (Map.Entry<String, T> entry : value.entrySet()) {
        String entryKey = entry.getKey();
        if (entryKey == null) {
          throw Utils.parameterError(method, p, "Field map contained null key.");
        }
        T entryValue = entry.getValue();
        if (entryValue == null) {
          throw Utils.parameterError(
              method, p, "Field map contained null value for key '" + entryKey + "'.");
        }

        String fieldEntry = valueConverter.convert(entryValue);
        if (fieldEntry == null) {
          throw Utils.parameterError(
              method,
              p,
              "Field map value '"
                  + entryValue
                  + "' converted to null by "
                  + valueConverter.getClass().getName()
                  + " for key '"
                  + entryKey
                  + "'.");
        }

        builder.addFormField(entryKey, fieldEntry, encoded);
      }
    }
  }

Parameterhandelr.Field 相比就是会遍历每个 Map 中的 Key-Value,然后分别调用 RequestBuilder#addFormField() 方法添加 Field 参数。

@Part

方法中必须有 @Multipart 修饰的方法才能,使用带 @Part 注解的参数,他表示是 Multipart 类型 RequestBody 中的一部分。

Java 复制代码
    @Nullable
    private ParameterHandler<?> parseParameterAnnotation(
        int p, Type type, Annotation[] annotations, Annotation annotation) {
      // ...
      if (annotation instanceof Part) {
        validateResolvableType(p, type);
        // 必须有 @Multipart 修饰
        if (!isMultipart) {
          throw parameterError(
              method, p, "@Part parameters can only be used with multipart encoding.");
        }
        Part part = (Part) annotation;
        gotPart = true;
        // 从注解中获取 Part 的名字
        String partName = part.value();
        Class<?> rawParameterType = Utils.getRawType(type);
        // 如果名字为空
        if (partName.isEmpty()) {
          // 如果类型是迭代器
          if (Iterable.class.isAssignableFrom(rawParameterType)) {
            if (!(type instanceof ParameterizedType)) {
              throw parameterError(
                  method,
                  p,
                  rawParameterType.getSimpleName()
                      + " must include generic type (e.g., "
                      + rawParameterType.getSimpleName()
                      + "<String>)");
            }
            ParameterizedType parameterizedType = (ParameterizedType) type;
            // 获取迭代器泛型对象的类型
            Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
            // 如果迭代器类型的参数不是 MultipartBody.Part 类型报错
            if (!MultipartBody.Part.class.isAssignableFrom(Utils.getRawType(iterableType))) {
              throw parameterError(
                  method,
                  p,
                  "@Part annotation must supply a name or use MultipartBody.Part parameter type.");
            }
            return ParameterHandler.RawPart.INSTANCE.iterable();
          } else if (rawParameterType.isArray()) {
            // 数组处理
            Class<?> arrayComponentType = rawParameterType.getComponentType();
            if (!MultipartBody.Part.class.isAssignableFrom(arrayComponentType)) {
              throw parameterError(
                  method,
                  p,
                  "@Part annotation must supply a name or use MultipartBody.Part parameter type.");
            }
            return ParameterHandler.RawPart.INSTANCE.array();
          } else if (MultipartBody.Part.class.isAssignableFrom(rawParameterType)) {
            // 其他类型的处理
            return ParameterHandler.RawPart.INSTANCE;
          } else {
            throw parameterError(
                method,
                p,
                "@Part annotation must supply a name or use MultipartBody.Part parameter type.");
          }
        } else {
          // 以下的逻辑表示名字不为空
          
          // 构建一个带 PartName 和  Encoding 的 Header。
          Headers headers =
              Headers.of(
                  "Content-Disposition",
                  "form-data; name=\"" + partName + "\"",
                  "Content-Transfer-Encoding",
                  part.encoding());
          // 如果参数类型是迭代器
          if (Iterable.class.isAssignableFrom(rawParameterType)) {
            if (!(type instanceof ParameterizedType)) {
              throw parameterError(
                  method,
                  p,
                  rawParameterType.getSimpleName()
                      + " must include generic type (e.g., "
                      + rawParameterType.getSimpleName()
                      + "<String>)");
            }
            ParameterizedType parameterizedType = (ParameterizedType) type;
            // 获取迭代器泛型的类型
            Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
            // 如果迭代器泛型类型是 MultipartBody.Part,就报错
            if (MultipartBody.Part.class.isAssignableFrom(Utils.getRawType(iterableType))) {
              throw parameterError(
                  method,
                  p,
                  "@Part parameters using the MultipartBody.Part must not "
                      + "include a part name in the annotation.");
            }
            // 获取将迭代器泛型类型转 RequestBody 的 Converter
            Converter<?, RequestBody> converter =
                retrofit.requestBodyConverter(iterableType, annotations, methodAnnotations);
            return new ParameterHandler.Part<>(method, p, headers, converter).iterable();
          } else if (rawParameterType.isArray()) {
            // 数组处理
            Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
            if (MultipartBody.Part.class.isAssignableFrom(arrayComponentType)) {
              throw parameterError(
                  method,
                  p,
                  "@Part parameters using the MultipartBody.Part must not "
                      + "include a part name in the annotation.");
            }
            Converter<?, RequestBody> converter =
                retrofit.requestBodyConverter(arrayComponentType, annotations, methodAnnotations);
            return new ParameterHandler.Part<>(method, p, headers, converter).array();
          } else if (MultipartBody.Part.class.isAssignableFrom(rawParameterType)) {
            throw parameterError(
                method,
                p,
                "@Part parameters using the MultipartBody.Part must not "
                    + "include a part name in the annotation.");
          } else {
            // 其他普通类型处理
            Converter<?, RequestBody> converter =
                retrofit.requestBodyConverter(type, annotations, methodAnnotations);
            return new ParameterHandler.Part<>(method, p, headers, converter);
          }
        }

      }
      // ...
      
   }

@Part@Query 注解一样都是单独处理数组和迭代器类型的数据的,处理方式也类似,后面的描述也就不分析了,由于 Part 类型是必须要一个 name,这个 name 可以从 @Part 注解中获取,也可以从参数类型为 MultipartBody.Part 中获取。所以代码就分为了 @Part 中的 name 为空和不为空两种处理逻辑。

@Part 中的 name 为空:

  1. 参数类型必须是 MultipartBody.Part
  2. 参数处理器是 ParameterHandler.RawPart,使用的是一个单例对象。

@Part 中的 name 不为空:

  1. 参数类型不能是 MultipartBody.Part
  2. @Part 中的 nameencoding 参数构建成一个新的 Headers
  3. 需要找到一个将参数类型转换成 RequestBodyConverter
  4. 参数处理器是 ParameterHandler.Part

我们先看看 name 不为空的参数处理器 ParameterHandler.RawPart 的源码:

Java 复制代码
   static final class RawPart extends ParameterHandler<MultipartBody.Part> {
    static final RawPart INSTANCE = new RawPart();

    private RawPart() {}

    @Override
    void apply(RequestBuilder builder, @Nullable MultipartBody.Part value) {
      if (value != null) { // Skip null values.
        builder.addPart(value);
      }
    }
  }

直接通过 RequestBuilder#addPart() 方法添加 Part

然后再看看 name 为空的参数处理器 ParameterHandelr.Part 的源码:

Java 复制代码
  static final class Part<T> extends ParameterHandler<T> {
    private final Method method;
    private final int p;
    private final okhttp3.Headers headers;
    private final Converter<T, RequestBody> converter;

    Part(Method method, int p, okhttp3.Headers headers, Converter<T, RequestBody> converter) {
      this.method = method;
      this.p = p;
      this.headers = headers;
      this.converter = converter;
    }

    @Override
    void apply(RequestBuilder builder, @Nullable T value) {
      if (value == null) return; // Skip null values.

      RequestBody body;
      try {
        // 转换成 RequestBody
        body = converter.convert(value);
      } catch (IOException e) {
        throw Utils.parameterError(method, p, "Unable to convert " + value + " to RequestBody", e);
      }
      // 添加 part,注意这里传递了一个 PartBody 的 header
      builder.addPart(headers, body);
    }
  }

RawPart 中不同的是,首先会通过 Converter 将参数转换成 RequestBody,然后调用 RequestBuilder#addPart() 方法,注意这里还把之前生成的 Headers 也传递进去了,它会作为 MultipartBody.Part 中的 Headers

@PartMap

@PartMap@Part 的关系,就相当于 @QueryMap@Query 的关系,跳过源码分析了,直接看看它的参数处理器的类 ParameterHandler.PartMap

Java 复制代码
  static final class PartMap<T> extends ParameterHandler<Map<String, T>> {
    private final Method method;
    private final int p;
    private final Converter<T, RequestBody> valueConverter;
    private final String transferEncoding;

    PartMap(
        Method method, int p, Converter<T, RequestBody> valueConverter, String transferEncoding) {
      this.method = method;
      this.p = p;
      this.valueConverter = valueConverter;
      this.transferEncoding = transferEncoding;
    }

    @Override
    void apply(RequestBuilder builder, @Nullable Map<String, T> value) throws IOException {
      if (value == null) {
        throw Utils.parameterError(method, p, "Part map was null.");
      }
      // 遍历 Map
      for (Map.Entry<String, T> entry : value.entrySet()) {
        String entryKey = entry.getKey();
        // Key 不能为空
        if (entryKey == null) {
          throw Utils.parameterError(method, p, "Part map contained null key.");
        }
        T entryValue = entry.getValue();
        // Value 不能为空
        if (entryValue == null) {
          throw Utils.parameterError(
              method, p, "Part map contained null value for key '" + entryKey + "'.");
        }
        // 构建 Part 的 Header。
        okhttp3.Headers headers =
            okhttp3.Headers.of(
                "Content-Disposition",
                "form-data; name=\"" + entryKey + "\"",
                "Content-Transfer-Encoding",
                transferEncoding);
        // 添加 part
        builder.addPart(headers, valueConverter.convert(entryValue));
      }
    }
  }

@Part 相比,也就是添加多个 Part BodyRequestBuilder 中。

@Body

描述请求的 Request Body,源码如下:

Java 复制代码
    @Nullable
    private ParameterHandler<?> parseParameterAnnotation(
        int p, Type type, Annotation[] annotations, Annotation annotation) {
      // ...
      if (annotation instanceof Body) {
        validateResolvableType(p, type);
        // 不能是 Multipart 和 FormUrlEncoded
        if (isFormEncoded || isMultipart) {
          throw parameterError(
              method, p, "@Body parameters cannot be used with form or multi-part encoding.");
        }
        // 不能重复添加 Body
        if (gotBody) {
          throw parameterError(method, p, "Multiple @Body method annotations found.");
        }

        Converter<?, RequestBody> converter;
        try {
          // 查找 RequestBody 的 Converter.
          converter = retrofit.requestBodyConverter(type, annotations, methodAnnotations);
        } catch (RuntimeException e) {
          // Wide exception range because factories are user code.
          throw parameterError(method, e, p, "Unable to create @Body converter for %s", type);
        }
        gotBody = true;
        return new ParameterHandler.Body<>(method, p, converter);

      } 
      // ...
      
   }

简单描述一下上面的代码:

  1. 方法不能有 @Multipart 或者 @FormUrlEncoded 注解修饰。
  2. 不能添加多个 Body
  3. 查找 Request BodyConverter
  4. 参数处理器是 ParameterHandler.Body

再看看 ParameterHandler.Body 的源码:

Java 复制代码
  static final class Body<T> extends ParameterHandler<T> {
    private final Method method;
    private final int p;
    private final Converter<T, RequestBody> converter;

    Body(Method method, int p, Converter<T, RequestBody> converter) {
      this.method = method;
      this.p = p;
      this.converter = converter;
    }

    @Override
    void apply(RequestBuilder builder, @Nullable T value) {
      if (value == null) {
        throw Utils.parameterError(method, p, "Body parameter value must not be null.");
      }
      RequestBody body;
      try {
        body = converter.convert(value);
      } catch (IOException e) {
        throw Utils.parameterError(method, e, p, "Unable to convert " + value + " to RequestBody");
      }
      builder.setBody(body);
    }
  }

我自己都不想解释了,代码很简单,自己看。。。

最后

终于看完了方法参数解析的代码,很多的代码都是一些模版代码,阅读起来很简单,到这里 RequestFactory 的代码也就分析完了,也就是 Request 的解析,其中包括方法的解析和方法参数的解析。

相关推荐
Estar.Lee1 小时前
时间操作[计算时间差]免费API接口教程
android·网络·后端·网络协议·tcp/ip
找藉口是失败者的习惯2 小时前
从传统到未来:Android XML布局 与 Jetpack Compose的全面对比
android·xml
Jinkey3 小时前
FlutterBasic - GetBuilder、Obx、GetX<Controller>、GetxController 有啥区别
android·flutter·ios
大白要努力!4 小时前
Android opencv使用Core.hconcat 进行图像拼接
android·opencv
天空中的野鸟5 小时前
Android音频采集
android·音视频
小白也想学C6 小时前
Android 功耗分析(底层篇)
android·功耗
曙曙学编程7 小时前
初级数据结构——树
android·java·数据结构
闲暇部落9 小时前
‌Kotlin中的?.和!!主要区别
android·开发语言·kotlin
诸神黄昏EX11 小时前
Android 分区相关介绍
android
大白要努力!12 小时前
android 使用SQLiteOpenHelper 如何优化数据库的性能
android·数据库·oracle