图解 Retrofit 之 ServiceMethod

通过 Retrofit + RxAndroid 实践总结,我们已经了解到了 Retrofit 的基本用法,为了知其所以然,我们以图解加源码的方式从 Service Method 入手,逐步拨开 Retrofit 的神秘面纱。

首先以官方网站的示例代码为例,看一下一个 Service Method 的组成部分:

ServiceMethod 使用了 Builder 模式,先来看 ServiceMethod.Builder 的构造方法:

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

构造方法接受两个参数 - retrofitmethod,然后通过调用 Method 类的方法获取以下数据:

final Annotation[] methodAnnotations;
final Annotation[][] parameterAnnotationsArray;
final Type[] parameterTypes;

methodAnnotations 就对应着上例中的 @GET("users/{user}/repos",由 parseMethodAnnotation 负责解析。

method parameter 也有对应的 annotation type,由对应的 ParameterHandler 进行处理,例如上例中的 @Path 就对应着 class Path<T> extends ParameterHandler<T>

一个 parameter 可以有多个 annotation,所以 parameterAnnotationsArray 是一个二维数组 - Annotation[][]

ServiceMethod.Builder 最终会 build 出一个 ServiceMethod 实例,我们先来看 method annotations 的解析过程。

method annotations

ServiceMethod.Builder#build

for (Annotation annotation : methodAnnotations) {
  parseMethodAnnotation(annotation);
}  

如下图所示,parseMethodAnnotation 方法根据 method annotation 的类型(蓝色框内)生成一个 request 所需要的数据(褐色框内)。

其中 httpMethod 不能为空,如果 hasBody == false,那么 isMultipartisFormEncoded 也必须为 false,而且两者互斥不能同时为 true,否则会抛出异常。

解析完 method annotations 之后,再来解析 parameters(代码示例中的 @Path("user") String user)。

parameters

每一个 parameter annotation 类型都有对应的 ParameterHandler,method parameters 解析完成之后生成一个数组 - ParameterHandler[] parameterHandlers,这个数组在后面构建 request 的时候会用到。

解析过程中会验证 @Path value 的合法性,并确保 value 在 relativeUrlParamNames由 method annotations 生成) 中。

// Upper and lower characters, digits, underscores, and hyphens, starting with a character.
static final String PARAM = "[a-zA-Z][a-zA-Z0-9_-]*";
static final Pattern PARAM_URL_REGEX = Pattern.compile("\\{(" + PARAM + ")\\}");
static final Pattern PARAM_NAME_REGEX = Pattern.compile(PARAM);

private void validatePathName(int p, String name) {
  if (!PARAM_NAME_REGEX.matcher(name).matches()) {
    throw parameterError(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(p, "URL \"%s\" does not contain \"{\%s}\".", relativeUrl, name);
  }
}

parseParameterAnnotation 方法在解析 Url 类型的 parameter annotation 时会判断 parameter 类型,由于 Retrofit 本身没有依赖 Android SDK,所以无法像 HttpUrl.class 那样获取 Uri.class,但是 Retrofit 的实现很巧妙,学到了:)

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.");
}

method annotations 以及 parameters 都解析完成后,我们再回到 service method 的 callAdapter。

CallAdapter

想要了解 CallAdapter 的功能,我们需要先从 Retrofitcreate 方法开始分析。

create 方法使用动态代理把 service method 的调用转发给一个 InvocationHandler

ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);

由以上代码可以看出,CallAdapter 会把一个 Call 类型适配为用户定义的 service method 的 return type。

举个例子,如果我们配合 RxAndroid 使用 Retrofit,service method 的返回值类型会由 Call 类型变成 Observable 类型,这个转换其实就由 RxJavaCallAdapterFactory 来实现。

Retrofit retrofit = new Retrofit.Builder()      
    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
    .baseUrl(BASE_URL) .build();

由以上代码可以看出,RxJavaCallAdapterFactory 是通过 Retrofit.BuilderaddCallAdapterFactory 方法传递给 Retrofit,而 create 方法中 adapt 的执行者却是 ServiceMethod,也就是说 ServiceMethodCallAdapter 是由 Retrofit 提供的,两者的交互关系如下图所示:

更新时间:

留下评论