美文网首页
Android Retrofit 源码阅读笔记(二)

Android Retrofit 源码阅读笔记(二)

作者: 头秃到底 | 来源:发表于2023-12-20 19:21 被阅读0次

    Retrofit 源码阅读笔记(二)

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

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

    方法参数解析

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

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

        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 会冲突。

        @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 源码:

      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 就是前面一篇文章中提到的相对路径占位符。我这里举一个例子:

    @POST("/a/{name}/c")  
    Call<String> foo(@Path("name") String path);
    
    

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

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

        @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 的源码:

      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 是可以重复的,如果重复的话就当成是一个数组处理。

        @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 的源码:

       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,我们直接看看它的源代码:

       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

        @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 的源码:

      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 源码:

       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 的源码实现:

      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 的参数。

        @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 的源码实现:

       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,看看它的源码:

    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 中的一部分。

        @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 的源码:

       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 的源码:

      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

      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,源码如下:

        @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 的源码:

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

    相关文章

      网友评论

          本文标题:Android Retrofit 源码阅读笔记(二)

          本文链接:https://www.haomeiwen.com/subject/frqxndtx.html