Retrofit2 注解如何处理

作者: HarryXR | 来源:发表于2017-03-01 10:41 被阅读1564次

    近年来,Retrofit非常火,配合Rxjava简直就是Android开发神器,今天谈谈它到底是如何处理注解的。

    Retrofit注解分为方法注解和参数注解,具体就不多说。

    Retrofit:compile 'com.squareup.retrofit2:retrofit:2.2.0'

    直接进源码

    • Retrofit.create
    public <T> T create(final Class<T> service) {
        Utils.validateServiceInterface(service);
        if (validateEagerly) {
          eagerlyValidateMethods(service);
        }
        return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
            new InvocationHandler() {
              private final Platform platform = Platform.get();
    
              @Override public Object invoke(Object proxy, Method method, Object[] args)
                  throws Throwable {
                // If the method is a method from Object then defer to normal invocation.
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args);
                }
                if (platform.isDefaultMethod(method)) {
                  return platform.invokeDefaultMethod(method, service, proxy, args);
                }
    //此处是重点
                ServiceMethod<Object, Object> serviceMethod =
                    (ServiceMethod<Object, Object>) loadServiceMethod(method);
                OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
                return serviceMethod.callAdapter.adapt(okHttpCall);
              }
            });
      }
    

    首先看Proxy.newProxyInstance:

    try {
                return getProxyClass(loader, interfaces)
                        .getConstructor(InvocationHandler.class)
                        .newInstance(invocationHandler);
            } catch (NoSuchMethodException e) {
                cause = e;
            } catch (IllegalAccessException e) {
                cause = e;
            } catch (InstantiationException e) {
                cause = e;
            } catch (InvocationTargetException e) {
                cause = e;
            }
    

    Proxy实例化就是返回一个InvocationHandler的示例,类似代理模式;

    回到上面的代码,Retrofit.create就是创建了一个InvocationHandler,InvocationHandler执行invoke返回ServiceMethod,

    注解就是在ServiceMethod中处理的

    • ServiceMethod
      ServiceMethod是Builder模式,靠内部类Builder实例化
    1. Builder初始化方法注解、参数注解数组
    Builder(Retrofit retrofit, Method method) {
          this.retrofit = retrofit;
          this.method = method;
          this.methodAnnotations = method.getAnnotations();
          this.parameterTypes = method.getGenericParameterTypes();
          this.parameterAnnotationsArray = method.getParameterAnnotations();
        }
    
    1. ServiceMethod.Builder.build()
    public ServiceMethod build() {
          callAdapter = createCallAdapter();
          responseType = callAdapter.responseType();
         ...
          responseConverter = createResponseConverter();
    
          for (Annotation annotation : methodAnnotations) {
            parseMethodAnnotation(annotation);//方法注解处理
          }
    
         ....
    
          int parameterCount = parameterAnnotationsArray.length;
          parameterHandlers = new ParameterHandler<?>[parameterCount];
          for (int p = 0; p < parameterCount; p++) {
            Type parameterType = parameterTypes[p];
            if (Utils.hasUnresolvableType(parameterType)) {
              throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
                  parameterType);
            }
    
            Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
       ......
    
            parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);//参数注解处理
          }
    .....
    
          return new ServiceMethod<>(this);
        }
    

    只看两个方法, parseMethodAnnotation(annotation)和parseParameter,分别处理方法和参数注解

    • parseMethodAnnotation方法注解处理
    private void parseMethodAnnotation(Annotation annotation) {
          if (annotation instanceof DELETE) {
            parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
          } else if (annotation instanceof GET) {
            parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
          } else if (annotation instanceof HEAD) {
            parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
            if (!Void.class.equals(responseType)) {
              throw methodError("HEAD method must use Void as response type.");
            }
          } else if (annotation instanceof PATCH) {
            parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
          } else if (annotation instanceof POST) {
            parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
          } else if (annotation instanceof PUT) {
            parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
          } else if (annotation instanceof OPTIONS) {
            parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
          } else if (annotation instanceof HTTP) {
            HTTP http = (HTTP) annotation;
            parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
          } else if (annotation instanceof retrofit2.http.Headers) {
            String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
            if (headersToParse.length == 0) {
              throw methodError("@Headers annotation is empty.");
            }
            headers = parseHeaders(headersToParse);
          } 
        }
    

    方法注解实现 parseHttpMethodAndPath:

    赋值httpMethod和relativeUrl,请求方法和url:
    private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
        ...
          this.httpMethod = httpMethod;
          this.hasBody = hasBody;
      ....
          this.relativeUrl = value;
          this.relativeUrlParamNames = parsePathParameters(value);
        }
    

    看parsePathParameters:

    将方法注解值放入Set中
    static Set<String> parsePathParameters(String path) {
        Matcher m = PARAM_URL_REGEX.matcher(path);
        Set<String> patterns = new LinkedHashSet<>();
        while (m.find()) {
          patterns.add(m.group(1));
        }
        return patterns;
      }
    
    • parseParameter-参数注解处理
    循环处理每个注解
    private ParameterHandler<?> parseParameter(
            int p, Type parameterType, Annotation[] annotations) {
          ParameterHandler<?> result = null;
          for (Annotation annotation : annotations) {
           //处理单个注解
            ParameterHandler<?> annotationAction = parseParameterAnnotation(
                p, parameterType, annotations, annotation);
            ...
            if (result != null) {
              throw parameterError(p, "Multiple Retrofit annotations found, only one allowed.");
            }
    
            result = annotationAction;
          }
    
    ...
          return result;
        }
    

    处理单个注解:

    这里我保留了Query,Path,Filed,Body注解的处理,其他注解类似;
    
    private ParameterHandler<?> parseParameterAnnotation(
            int p, Type type, Annotation[] annotations, Annotation annotation) {
         ...
            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();
            } else {
              throw parameterError(p,
                  "@Url must be okhttp3.HttpUrl, String, java.net.URI, or android.net.Uri type.");
            }
    
          } else if (annotation instanceof Path) {
            if (gotQuery) {
        ...
            gotPath = true;
    
            Path path = (Path) annotation;
            String name = path.value();
            validatePathName(p, name);
    
            Converter<?, String> converter = retrofit.stringConverter(type, annotations);
            return new ParameterHandler.Path<>(name, converter, path.encoded());
    
          } else if (annotation instanceof Query) {
            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(p, rawParameterType.getSimpleName()
                    + " must include generic type (e.g., "
                    + rawParameterType.getSimpleName()
                    + "<String>)");
              }
              ParameterizedType parameterizedType = (ParameterizedType) type;
              Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
              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);
            }
    
          } 
           ...
           else if (annotation instanceof Field) {
            if (!isFormEncoded) {
              throw parameterError(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(p, rawParameterType.getSimpleName()
                    + " must include generic type (e.g., "
                    + rawParameterType.getSimpleName()
                    + "<String>)");
              }
              ParameterizedType parameterizedType = (ParameterizedType) type;
              Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
              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);
            }
    
          } ...
           ...
    else if (annotation instanceof Body) {
            if (isFormEncoded || isMultipart) {
              throw parameterError(p,
                  "@Body parameters cannot be used with form or multi-part encoding.");
            }
            if (gotBody) {
              throw parameterError(p, "Multiple @Body method annotations found.");
            }
    
            Converter<?, RequestBody> converter;
            try {
              converter = retrofit.requestBodyConverter(type, annotations, methodAnnotations);
            } catch (RuntimeException e) {
              // Wide exception range because factories are user code.
              throw parameterError(e, p, "Unable to create @Body converter for %s", type);
            }
            gotBody = true;
            return new ParameterHandler.Body<>(converter);
          }
    
          return null; // Not a Retrofit annotation.
        }
    
    

    该方法针对注解类型,返回不同的ParameterHandler内部类实例,比如ParameterHandler.Body,ParameterHandler.Query。

    就说这么多,注解处理流程大概就是这么多。

    相关文章

      网友评论

        本文标题:Retrofit2 注解如何处理

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