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 的解析,其中包括方法的解析和方法参数的解析。

相关推荐
无极程序员1 小时前
PHP常量
android·ide·android studio
萌面小侠Plus2 小时前
Android笔记(三十三):封装设备性能级别判断工具——低端机还是高端机
android·性能优化·kotlin·工具类·低端机
慢慢成长的码农2 小时前
Android Profiler 内存分析
android
大风起兮云飞扬丶2 小时前
Android——多线程、线程通信、handler机制
android
L72562 小时前
Android的Handler
android
清风徐来辽2 小时前
Android HandlerThread 基础
android
HerayChen3 小时前
HbuildderX运行到手机或模拟器的Android App基座识别不到设备 mac
android·macos·智能手机
顾北川_野3 小时前
Android 手机设备的OEM-unlock解锁 和 adb push文件
android·java
hairenjing11233 小时前
在 Android 手机上从SD 卡恢复数据的 6 个有效应用程序
android·人工智能·windows·macos·智能手机
小黄人软件4 小时前
android浏览器源码 可输入地址或关键词搜索 android studio 2024 可开发可改地址
android·ide·android studio