Retrofit源码解析三——对接口方法参数注解的处理

private @Nullable ParameterHandler<?> parseParameter(
        int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) {
      ParameterHandler<?> result = null;
      if (annotations != null) {
        for (Annotation annotation : annotations) {
	//核心就是这一句,实际上就是把前面获取到的参数类型数组与注解数组匹配
          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;
        }
      }

      if (result == null) {
        if (allowContinuation) {
          try {
            if (Utils.getRawType(parameterType) == Continuation.class) {
              isKotlinSuspendFunction = true;
              return null;
            }
          } catch (NoClassDefFoundError ignored) {
          }
        }
        throw parameterError(method, p, "No Retrofit annotation found.");
      }

      return result;
    }

紧接上文,我们来看看Service接口中的参数注解是怎样处理的。

涉及到的两项验证

不确定的类型检查

首先来看validateResolvableType方法,其实是对参数的类型包括范型的检查。

private void validateResolvableType(int p, Type type) {
      if (Utils.hasUnresolvableType(type)) {
        throw parameterError(method, p,
            "Parameter type must not include a type variable or wildcard: %s", type);
      }
    }

这里巧妙使用了递归来判断类型/范型是否不可取:

static boolean hasUnresolvableType(@Nullable Type type) {
    if (type instanceof Class<?>) {
      return false;
    }
    if (type instanceof ParameterizedType) {
      ParameterizedType parameterizedType = (ParameterizedType) type;
      for (Type typeArgument : parameterizedType.getActualTypeArguments()) {
        if (hasUnresolvableType(typeArgument)) {
          return true;
        }
      }
      return false;
    }
    if (type instanceof GenericArrayType) {
      return hasUnresolvableType(((GenericArrayType) type).getGenericComponentType());
    }
    if (type instanceof TypeVariable) {
      return true;
    }
    if (type instanceof WildcardType) {
      return true;
    }
    String className = type == null ? "null" : type.getClass().getName();
    throw new IllegalArgumentException("Expected a Class, ParameterizedType, or "
        + "GenericArrayType, but <" + type + "> is of type " + className);
  }

看到这真的醉了,特意去了解了一下这里面都是什么东西然后发现,只有两类Type是不可取的:

  1. TypeVariable,写法如:TestReflect<T>
  2. WildcardType,写法如:List<? extends TestReflect>

那么我们可以看到,上面两种类型的type都是类型不确定的。

pathName路径名称校验
private void validatePathName(int p, String name) {
      if (!PARAM_NAME_REGEX.matcher(name).matches()) {
        throw parameterError(method, p, "@Path parameter name must match %s. Found: %s",
            PARAM_URL_REGEX.pattern(), name);
      }
      // Verify URL replacement name is actually present in the URL path.
      if (!relativeUrlParamNames.contains(name)) {
        throw parameterError(method, p, "URL \"%s\" does not contain \"{%s}\".", relativeUrl, name);
      }
    }

对各种注解的处理

其实都差不多,这里以第一个Url注解为例。

Url注解
if (annotation instanceof Url) {
        validateResolvableType(p, type);
        if (gotUrl) {
          throw parameterError(method, p, "Multiple @Url method annotations found.");
        }
        if (gotPath) {
          throw parameterError(method, p, "@Path parameters may not be used with @Url.");
        }
        if (gotQuery) {
          throw parameterError(method, p, "A @Url parameter must not come after a @Query.");
        }
        if (gotQueryName) {
          throw parameterError(method, p, "A @Url parameter must not come after a @QueryName.");
        }
        if (gotQueryMap) {
          throw parameterError(method, p, "A @Url parameter must not come after a @QueryMap.");
        }
        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.");
        }

      }

从上述代码可以看出,经过层层检查,最后就是返回了一个RelativeUrl对象,这个对象继承自ParameterHandler<Object>

abstract class ParameterHandler<T> {

  abstract void apply(RequestBuilder builder, @Nullable T value) throws IOException;

  final ParameterHandler<Iterable<T>> iterable() {
    return new ParameterHandler<Iterable<T>>() {
      @Override void apply(RequestBuilder builder, @Nullable Iterable<T> values)
          throws IOException {
        if (values == null) return; // Skip null values.

        for (T value : values) {
          ParameterHandler.this.apply(builder, value);
        }
      }
    };
  }

  final ParameterHandler<Object> array() {
    return new ParameterHandler<Object>() {
      @Override void apply(RequestBuilder builder, @Nullable Object values) throws IOException {
        if (values == null) return; // Skip null values.

        for (int i = 0, size = Array.getLength(values); i < size; i++) {
          //noinspection unchecked
          ParameterHandler.this.apply(builder, (T) Array.get(values, i));
        }
      }
    };
  }

这是一个抽象类,排除掉所有子类后,只含有三个方法:

  • apply(RequestBuilder builder, @Nullable T value):虚方法,组装RequestBuilder。那么所有的子类都需要实现这个方法,且每个子类的实现都不同。但是每种实现都只有一个共同的目的,就是给RequestBuilder中的属性赋值,这个值是从参数传递来的。
  • iterable():实例方法,循环组装builder。当参数为Iterable<T>类型时,通过循环调用apply方法来组装Builder。
  • array():同iterable方法,这不过这里是对数组的处理。

那么我们就可以知道,ParameterHandler这个类,或者说serverApi接口中定义的方法参数的注解的作用就是组装一个RequestBuilder,那么他所有的子类当然也是干了这么一件事,所以,这特么不就是外观模式么!那么这个RequestBuilder是用来生成谁呢?当然就是Okhttp中的Request

未完,来项目了,待续吧。。。

转载于:https://my.oschina.net/JiangTun/blog/3100933

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值