02_Retrofit工作流程-ServiceMethod 分析

114 阅读7分钟

ServiceMethod 分析

1. 创建动态代理对象

在 Retrofit 中,通过 Retrofit#create 方法可以创建接口的动态代理实现,使得接口方法的调用能够被拦截并进入指定的处理逻辑。

public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
            // 创建 ServiceMethod 对象,通过ServiceMethod解析方法注解及方法参数
            ServiceMethod serviceMethod = loadServiceMethod(method);

            // 创建 OkHttpCall 实例来处理 HTTP 请求
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);

            // 接收一个 `okHttpCall` 对象作为参数,并返回 Call 对象
            return serviceMethod.callAdapter.adapt(okHttpCall);
        }
    });
}
  • Proxy.newProxyInstance() 方法: 创建动态代理对象,需要指定类加载器、实现的接口数组和 InvocationHandler 对象,用于处理方法调用。
  • InvocationHandler.invoke() 方法: 当代理实例上的方法被调用时执行,负责拦截方法调用并进行相应处理。
  • ServiceMethod 对象创建: 通过 loadServiceMethod() 方法解析方法的注解,创建包含 HTTP 请求详细信息的 ServiceMethod 对象。
  • OkHttpCall 对象创建: 使用 ServiceMethod 对象和方法参数创建 OkHttpCall 实例,用于执行 HTTP 请求。
  • CallAdapter 对象创建:ServiceMethodbuild 方法中创建 CallAdapter 对象,用于将 OkHttpCall 适配为接口方法的返回类型。
  • callAdapter.adapt(okHttpCall): 它接收一个 okHttpCall 对象作为参数,并返回 Call 对象。代码示例中的 Call<User> call 就是通过 callAdapter.adapt(okHttpCall) 方法返回的。

loadServiceMethod() 方法

loadServiceMethod 方法用于加载并缓存 ServiceMethod 对象,以提高效率并避免重复创建对象。

private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();

ServiceMethod loadServiceMethod(Method method) {
    synchronized (serviceMethodCache) {
        ServiceMethod result = serviceMethodCache.get(method);
        if (result == null) {
            result = new ServiceMethod.Builder(this, method).build();
            serviceMethodCache.put(method, result);
        }
        return result;
    }
}

说明:

  • 缓存机制: 使用 LinkedHashMap 作为缓存容器,以方法 Method 对象作为键,ServiceMethod 对象作为值。

  • 同步锁: 使用 synchronized 关键字保证线程安全,在多线程环境下防止并发访问问题。

  • 创建新的 ServiceMethod 对象: 如果缓存中不存在对应的 ServiceMethod 对象,则通过 ServiceMethod.Builder 创建新的对象并存入缓存。

  • 返回结果: 返回已存在的或新创建的 ServiceMethod 对象,确保每个方法只被解析一次并缓存。

ServiceMethod 类详细分析

ServiceMethod 类负责解析方法的注解,并根据注解信息生成 HTTP 请求的详细信息。

final class ServiceMethod<T> {
    private final Retrofit retrofit;
    private final Method method;
    private final Annotation[] methodAnnotations;
    private final Type[] parameterTypes;
    private final Annotation[][] parameterAnnotationsArray;
    // 其他成员变量省略

    // 构造方法
    public Builder(Retrofit retrofit, Method method) {
        this.retrofit = retrofit;
        this.method = method;
        this.methodAnnotations = method.getAnnotations();
        this.parameterTypes = method.getGenericParameterTypes();
        this.parameterAnnotationsArray = method.getParameterAnnotations();
    }

    // 构建 ServiceMethod 对象
    public ServiceMethod build() {
        // 创建调用适配器
        callAdapter = createCallAdapter();
        // 获取响应类型 (responseType)
        responseType = callAdapter.responseType();
        if (responseType == Response.class || responseType == okhttp3.Response.class) {
            throw methodError("'" + Utils.getRawType(responseType).getName()
                    + "' is not a valid response body type. Did you mean ResponseBody?");
        }
        // 创建响应转换器 (responseConverter)
        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];
            if (parameterAnnotations == null) {
                throw parameterError(p, "No Retrofit annotation found.");
            }

            // 解析参数注解
            parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
        }

        return new ServiceMethod<>(this);
    }

    // 其他方法和内部类省略
}

说明:

  • Builder 构造方法: 初始化 ServiceMethod 对象的各项属性,包括 Retrofit 实例、方法对象、方法的注解数组和参数类型数组。

  • build() 方法: 构建 ServiceMethod 对象的过程,包括创建调用适配器、获取响应类型、创建响应转换器、解析方法注解以及处理方法参数。

  • 解析方法注解 (parseMethodAnnotation() 方法): 根据不同的 HTTP 方法注解,解析出对应的 HTTP 方法和路径信息。

  • 处理方法参数: 遍历方法的参数注解数组,通过 parseParameter() 方法解析每个参数的注解信息,并构建相应的 ParameterHandler 对象。

  • 通过 Builder 模式封装对象构建过程: 提供了灵活和可扩展的方式,使得 Retrofit 可以根据接口方法的注解动态地生成并配置每个请求的处理逻辑。

ServiceMethod解析参数过程

在 Retrofit 的 ServiceMethod 类中,参数解析过程涉及多个方法,每个方法负责解析不同类型的注解或参数信息。下面逐一分析这些方法的具体作用和实现细节:

1. 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);
    }
    // 其他 HTTP 方法的解析,如 POST、PUT 等
}
  • 作用: 解析方法上的 HTTP 方法注解(如 GETPOSTPUTDELETE 等),并根据注解提供的值解析 HTTP 方法和路径信息。

  • 实现细节:

    • 根据注解类型判断当前 HTTP 方法。
    • 调用 parseHttpMethodAndPath 方法设置 HTTP 方法、路径信息和是否有请求体(body)的标志。

2. parseHttpMethodAndPath 方法

private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
    if (this.httpMethod != null) {
        throw methodError("Only one HTTP method is allowed. Found: %s and %s.", this.httpMethod, httpMethod);
    }
    this.httpMethod = httpMethod;
    this.hasBody = hasBody;

    if (!value.isEmpty()) {
        // 解析相对 URL 路径
        this.relativeUrl = value;
        // 解析路径参数名称
        this.relativeUrlParamNames = parsePathParameters(value);
    }
}
  • 作用: 解析 HTTP 方法和路径注解,并设置到当前 ServiceMethod 对象中。

  • 实现细节:

    • 检查是否已经设置了 HTTP 方法,如果已设置则抛出异常。
    • 设置当前 HTTP 方法类型和是否有请求体的标志。
    • 如果提供了路径值,则解析相对 URL 路径,并提取路径参数的名称。

3. parseHeaders 方法

private void parseHeaders(String[] headers) {
    if (headers != null) {
        for (String header : headers) {
            int colon = header.indexOf(':');
            if (colon == -1 || colon == 0 || colon == header.length() - 1) {
                throw methodError("@Headers value must be in the form "Name: Value". Found: "%s"", header);
            }
            String headerName = header.substring(0, colon);
            String headerValue = header.substring(colon + 1).trim();
            if ("Content-Type".equalsIgnoreCase(headerName)) {
                this.contentType = MediaType.parse(headerValue);
            } else {
                if (this.headers == null) {
                    this.headers = new Headers.Builder();
                }
                this.headers.add(headerName, headerValue);
            }
        }
    }
}
  • 作用: 解析方法上的 @Headers 注解,并将解析结果设置到当前 ServiceMethod 对象中的请求头部信息。

  • 实现细节:

    • 遍历 @Headers 注解中的每个字符串。
    • 检查每个字符串是否符合 "Name: Value" 的格式,如果不符则抛出异常。
    • 解析出头部的名称和值,如果是 Content-Type 头部则解析为 MediaType 对象;否则添加到请求头部中。

4. 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 (annotationAction != null) {
            if (result != null) {
                throw parameterError(p, "Multiple Retrofit annotations found, only one allowed.");
            }
            result = annotationAction;
        }
    }
    if (result == null) {
        throw parameterError(p, "No Retrofit annotation found.");
    }
    return result;
}
  • 作用: 解析方法参数上的 Retrofit 注解,并根据不同的注解类型创建对应的 ParameterHandler 对象。

  • 实现细节:

    • 遍历参数的所有注解。
    • 对每个注解调用 parseParameterAnnotation 方法解析,得到对应的 ParameterHandler 对象。
    • 如果找到多个 Retrofit 注解则抛出异常。
    • 如果没有找到 Retrofit 注解则抛出异常。

5. parseParameterAnnotation 方法

private ParameterHandler<?> parseParameterAnnotation(int p, Type parameterType, Annotation[] annotations, Annotation annotation) {
    if (annotation instanceof Query) {
        // 解析 @Query 注解
        
        // 验证参数类型是否可解析
        validateResolvableType(p, type);

        // 获取 @Query 注解的属性值
        Query query = (Query) annotation;
        String name = query.value();
        boolean encoded = query.encoded();

        // 获取参数的原始类型
        Class<?> rawParameterType = Utils.getRawType(type);

        // 标记已经获取了 @Query 注解
        gotQuery = true;

        // 处理 Iterable 类型的参数
        if (Iterable.class.isAssignableFrom(rawParameterType)) {
        // 如果参数是 Iterable 类型,则需要检查是否包含泛型类型
        if (!(type instanceof ParameterizedType)) {
        throw parameterError(
        method,
        p,
        rawParameterType.getSimpleName()
        + " must include generic type (e.g., "
        + rawParameterType.getSimpleName()
        + "<String>)");
        }

        // 获取 Iterable 的泛型类型
        ParameterizedType parameterizedType = (ParameterizedType) type;
        Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);

        // 创建转换器来将泛型类型转换为 String
        Converter<?, String> converter = retrofit.stringConverter(iterableType, annotations);

        // 返回一个处理 Iterable 类型参数的 ParameterHandler.Query 对象
        return new ParameterHandler.Query<>(name, converter, encoded).iterable();
    } else if (annotation instanceof Path) {
        // 解析 @Path 注解
        return parseParameterPath(p, parameterType, annotations);
    } else if (annotation instanceof Header) {
        // 解析 @Header 注解
        return parseParameterHeader(p, parameterType, annotations);
    } else if (annotation instanceof Field) {
        // 解析 @Field 注解
        return parseParameterField(p, parameterType, annotations);
    } else if (annotation instanceof Part) {
        // 解析 @Part 注解
        return parseParameterPart(p, parameterType, annotations);
    } else if (annotation instanceof Body) {
        // 解析 @Body 注解
        return parseParameterBody(p, parameterType, annotations);
    } else {
        return null; // 其他情况返回 null
    }
}
  • 作用: 根据特定的 Retrofit 注解类型,解析方法参数的注解并创建对应的 ParameterHandler 对象。

  • 实现细节:

    • 根据 annotation 的类型判断具体的 Retrofit 注解类型(如 @Query@Path@Header 等)。
    • 如果 annotation 不是 Retrofit 的注解之一,则返回 null。