Retrofit超详细源码分析

对于Retrofit源码,我们只需要懂Retrofit是如何拼接okhttp请求,以及如何发起Okhttp请求这两部分逻辑,那么对于retrofit基本上就是属于搞清楚的了,剩余的无非就是具体的代码细节是如何如何的而已。主流程弄清楚了,剩下的逻辑都是为主流程而服务的逻辑而已。

根据Retrofit的示例代码来进行源码分析

 Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("http://localhost:8080/")  
            .addConverterFactory(GsonConverterFactory.create())  
            .build();  
   ApiManager apiService = retrofit.create(ApiManager.class);
   Call<LoginResult> call = apiService.login("shuimu", "1234");  
   call.enqueue(new Callback<LoginResult>() {
       @Override  
       public void onResponse(Call<LoginResult> call, Response<LoginResult> response) {  
           //当前已经在主线程了
           if (response.isSuccess()) {  } else {  }  
       }  
       @Override  
       public void onFailure(Call<LoginResult> call, Throwable t) {  
          // 当前已经在主线程了
       }  
   }); 

上面是一个最简单的Retrofit的使用示例。首先先看retrofit的create方法。

  public <T> T create(final Class<T> service) {
    //1.检验传入的service接口的合法性。主要检查:
    //a.service是不是接口
    //b.接口上不能有泛型,接口的父类也不能有泛型。接口只能是接口方法存在泛型,接口类上不能存在泛型。
    validateServiceInterface(service);
    //service接口的具体实现由JVM自动生成的动态代理去实现的,动态代理对象持有了InvocationHandler的引用,同时动态代理实现了接口的方法,但是每个接口的方法内部都是调用InvocationHanlder的invoke方法去处理的。
    return (T) Proxy.newProxyInstance( service.getClassLoader(),new Class<?>[] {service},new InvocationHandler() {
              private final Platform platform = Platform.get();
              private final Object[] emptyArgs = new Object[0];
              //因为JVM生成的动态代理对象,把接口中所有的方法都交给了InvocationHanlder处理,因此invoke方法中必须存在处理所有方法的逻辑。
              @Override
              public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)  throws Throwable {
                // JVM生成的代理对象存在object父类的各种方法,例如toString,equals这些,这些直接执行即可,无需处理
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args);
                }
                args = args != null ? args : emptyArgs;
                //这里就是对所有的接口方法进行处理的地方。
                return platform.isDefaultMethod(method)
                    ? platform.invokeDefaultMethod(method, service, proxy, args)
                    : loadServiceMethod(method).invoke(args);
              }});
  }

从上面代码可知,Retrofit在创建API接口对象之前,先通过 validateServiceInterface()方法对API接口进行检查,之后,通过Proxy.newProxyInstance()来创建一个动态代理对象返回。对于动态代理我们都知道,动态代理肯定实现了指定接口中所有的方法,即API接口中所有的方法。而动态代理中接口的方法具体实现都是相同的,所有的接口方法内部都是调用Invocationhandler的invoke方法,把具体实现逻辑交给了InvocationHandler去实现。

总结:当用户通过动态代理调用API接口的方法时,动态代理则回调给InvocationHandler的invoke方法,InvocationHandler的invoke()会通过loadServiceMethod(method)把API接口方法转换成ServiceMethod。不同的API接口方法会在loadServiceMethod()中被解析成不同的ServiceMethod,每个被执行ServiceMethod.Invoke(args)所产生的结果就是该API接口方法的结果。

校验方法是最简单的,先快速看看validateServiceInterface()方法的逻辑:

 private void validateServiceInterface(Class<?> service) {
     //判断是不是一个接口,不是就抛出异常
    if (!service.isInterface()) { throw new IllegalArgumentException("API declarations must be interfaces.");  }
    Deque<Class<?>> check = new ArrayDeque<>(1);
    check.add(service);
     //判断接口上是否有泛型,以及接口所继承的父类是否有泛型
    while (!check.isEmpty()) {
      Class<?> candidate = check.removeFirst();
      if (candidate.getTypeParameters().length != 0) {
        StringBuilder message = new StringBuilder("Type parameters are unsupported on ").append(candidate.getName());
        if (candidate != service) {
          message.append(" which is an interface of").append(service.getName());
        }
        throw new IllegalArgumentException(message.toString());
      }
      Collections.addAll(check, candidate.getInterfaces());
    }
    //validateEagerly如果为true的话,就会解析当前传入的API Service接口内的所有方法。
    if (validateEagerly) {
      Platform platform = Platform.get();
      //循环获取接口中声明的方法,调用loadServiceMethod把除了接口中的静态方法和默认发之外的所有方法转换成ServiceMethod。
      for (Method method : service.getDeclaredMethods()) {
        if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {
          loadServiceMethod(method);
        }
      }
    }
  }
​

validateServiceInterface的验证中是能够提前对接口的所有方法进行转换的,但是要注意,retrofit从create方法到API接口中方法都是在主线程中执行的。或者说retrofit只有真正调用了enqueue(),enqueue()内部调用okhttp的enqueue()方法,这时才切的子线程。因此,提前对接口的所有方法进行解析是很耗时的

//例如:上面示例中,retrofit的创建,create方法,API接口的login方法都是在主线程中执行的  
ApiManager apiService = retrofit.create(ApiManager.class);API接口对象:
Call<LoginResult> call = apiService.login("shuimu", "1234");  
//只有这后面的call.enqueue方法,或者说该方法内部正在调用okhttp的enqueue()之后才是在子线程中执行
call.enqueue()

ServiceMethod.Invoke(args)所产生的结果就是该API接口方法的结果。因此有必要看看loadServiceMethod()是如何解析API接口从而生成ServiceMethod的。

在下面的loadServiceMethod()源码中能发现:
ServiceMethod是通过ServiceMethod.parseAnnotations(this, method);创建的。因为解析API接口并生成ServiceMethod是比较繁杂的任务,Retrofit会将生成的每个ServiceMethod缓存在serviceMethodCache这个Map集合中。

 ServiceMethod<?> loadServiceMethod(Method method) {
     //尝试从缓存中获取已经转换过的该接口中的方法
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null) return result;
 //第一次调用的方法,将转换成功ServiceMethod对象
    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = ServiceMethod.parseAnnotations(this, method);
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

解析API接口:ServiceMethod.parseAnnotations()的代码:

abstract class ServiceMethod<T> {
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {  
  //RequestFactory完成了API接口中,对于接口方法的解析逻辑。包括接口上的注解解析,接口方法参数的注解解析,RequestFactory根据API接口所描述的信息,自动完成HTTP 请求体的组装工作。
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
    Type returnType = method.getGenericReturnType();
    if (Utils.hasUnresolvableType(returnType)) {
      throw methodError( method,
          "Method return type must not include a type variable or wildcard: %s",
          returnType);
    }
    if (returnType == void.class) {
      throw methodError(method, "Service methods cannot return void.");
    }
    //返回ServiceMethod子类对象
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }
//ServiceMethod是抽象类,它有子类就是HttpServiceMethod实现了该方法。
  abstract @Nullable T invoke(Object[] args);
}

代码上能够发现ServiceMethod.parseAnnotations()中是做了两件事: (1)创建RequestFactory (2)通过 RequestFactory对象执行HttpServiceMethod.parseAnnotations()方法,该方法就会返回一个ServiceMethod的子类。 其中RequestFactory主要做API接口的解析工作,解析接口方法上面的注解和参数的注解,生成一个用于创建okhttp的request对象的工厂类。

RequestFactory的主流程解析

RequestFactory代码如下:

  static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
      //请求工厂RequestFactory通过构造者模式创建。所以具体的逻辑在这里面
    return new Builder(retrofit, method).build();
  }

RequestFactory.Builder中存在许多成员变量,这些成员变量是记录着解析API接口中Retrofit的各个注解的结果,方法返回值等等信息,以及还负责判断是否有正确使用Retrofit的各种注解

final Retrofit retrofit;
final Method method;//当前API请求方法的反射对象
final Annotation[] methodAnnotations;//方法上的注解
final Annotation[][] parameterAnnotationsArray;//每个参数上所有的注解的二维数组
final Type[] parameterTypes;//参数类型的数组
boolean gotField;//方法参数上是否存在@Field或者@FieldMap注解
boolean gotPart;//方法参数上是否存在@Part或者@PartMap注解
boolean gotBody;//方法参数上是否存在@Body注解
boolean gotPath;//方法参数上是否存在@Path注解
boolean gotQuery;//方法参数上是否存在@Query注解
boolean gotQueryName;//方法参数上是否存在@QueryName注解
boolean gotQueryMap;//方法参数上是否存在@QueryMap注解
boolean gotUrl;//方法参数上是否存在@Url注解
@Nullable String httpMethod;//当前请求方法的http Method
boolean hasBody;//当前请求报文是否有请求头
boolean isFormEncoded;//当前请求报文是否表单提交
boolean isMultipart;//当前请求报文是否是多表单提交
@Nullable String relativeUrl;//当前请求报文的相对路径
@Nullable Headers headers;//请求报文的请求头
@Nullable MediaType contentType;//请求报文的contentType
@Nullable Set<String> relativeUrlParamNames;//只能给该集合指定的路径占位符进行赋值
@Nullable ParameterHandler<?>[] parameterHandlers;//每个方法赋值时应该进行的添加操作。
boolean isKotlinSuspendFunction;//是否是挂起方法。
Builder(Retrofit retrofit, Method method) {
    //RequestFactory.Builder的构造方法在调用时,
    //保存了当前的retrofit对象。
      this.retrofit = retrofit;
    //当前执行的方法反射类
      this.method = method;
    //当前执行方法上的注解
      this.methodAnnotations = method.getAnnotations();
    //当前执行方法的参数的类型
      this.parameterTypes = method.getGenericParameterTypes();
    //当前执行方法的参数上的注解。parameterAnnotationsArray是二维数组,第一维表示参数的索引,第二维度才是参数上对应的注解数组。例如,假设一个方法上有两个参数是这样的:
//(@Url @Test @Nullable 参数类型1 参数名1,@Url @Test @Nullable 参数类型2 参数名2) 那么Method.getGenericParameterTypes()获取的二维数组假设为arr,则arr[0][]表示取出参数1上包含了该参数所有注解的数组,arr[1][]表示取出了参数2上包含了该参数所有注解的数组
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }
//RequestFactory.Builder对象创建号之后,直接调用build(),那么看看build方法的逻辑
 RequestFactory build() {
     //循环解析方法上的注解
      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }
    //方法上注解解析结束后,判断如果发现方法上没有HTTP method相关的注解,抛异常 
      if (httpMethod == null) { 
          throw methodError(method, "HTTP method annotation is required (e.g., @GET, @POST, etc.).");
       }
  //方法上注解解析结束后,hasBody字段是记录了方法上贴的HTTP method注解在HTTP规范中是属于不应该存在请求体的,但是却使用了@Multipart,@FormUrlEncoded等注解,则抛异常
      if (!hasBody) {
        if (isMultipart) {
          throw methodError( method,
              "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
        }
        if (isFormEncoded) {
          throw methodError( method,
              "FormUrlEncoded can only be specified on HTTP methods with "
            + "request body (e.g., @POST).");
        }
      }
//parameterAnnotationsArray在RequestFactory.Builder的构造方法中被赋值了,parameterAnnotationsArray的数组长度就是参数个数,第一维数组的索引对应参数的位置,第二维数组索引对应的是包含该位置上参数的所有注解的数组。
      int parameterCount = parameterAnnotationsArray.length;
     //根据参数个数创建每个参数的赋值器,按参数顺序保存在数组中。
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
          //上面循环解析方法上面的注解,这里则是循环解析 参数上的注解
        parameterHandlers[p] =
            parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
      }
    //相对路径可以通过每个HTTP Method注解添加,也可以通过@Url String 添加,反正都没有添加就抛异常
      if (relativeUrl == null && !gotUrl) {
        throw methodError(method, "Missing either @%s URL or @Url parameter.", httpMethod);
      }
     //不应该存在body的请求方法不应该使用@Body注解
      if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
        throw methodError(method, "Non-body HTTP method cannot contain @Body.");
      }
      if (isFormEncoded && !gotField) {
        throw methodError(method, "Form-encoded method must contain at least one @Field.");
      }
      if (isMultipart && !gotPart) {
        throw methodError(method, "Multipart method must contain at least one @Part.");
      }
    //经过上面的解析工作之后,所有的结果都会保存在RequestFactory对象中,等待后续的invoke方法调用就可以进行请求体组装以及让okhttp发起网络请求了
      return new RequestFactory(this);
    }

从上面的代码中能发现,RequestFactory先解析方法上的注解,并判断方法上注解的使用是否正确;然后RequestFactory解析方法参数上的注解,判断方法参数和方法注解两者协同使用是否正确。

parseMethodAnnotation()代码分析:

private void parseMethodAnnotation(Annotation annotation) {
    //判断方法上面的是什么注解,不同注解进行不同的操作  
    if (annotation instanceof DELETE) {
        //DELETE请求方式,传入annotation.value相对路径,delete请求方式不需要请求体
        parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
      } else if (annotation instanceof GET) {
        //GET请求方式,传入annotation.value相对路径,GET请求方式不需要请求体
        parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
      } else if (annotation instanceof HEAD) {
       //HEAD请求方式,传入annotation.value相对路径,HEAD请求方式不需要请求体
        parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
      } else if (annotation instanceof PATCH) {
       //PATCH请求方式,传入annotation.value相对路径,PATCH请求方式需要请求体       
        parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
      } else if (annotation instanceof POST) {
        //POST请求方式,传入annotation.value相对路径,POST请求方式需要请求体    
        parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
      } else if (annotation instanceof PUT) {
        //PUT请求方式,传入annotation.value相对路径,PUT请求方式需要请求体         
        parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
      } else if (annotation instanceof OPTIONS) {
        parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
      } else if (annotation instanceof HTTP) {
        //自定义请求方式,路径,是否需要body
        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(method, "@Headers annotation is empty.");
        }
        //根据注解上的请求头数组,直接创建okhttp请求头对象,并保持在RequestFactory中作为成员变量。
        headers = parseHeaders(headersToParse);
      } else if (annotation instanceof Multipart) {
        //如果该方法有两个注解,前一个是单表单,后面又来一个多表单,那肯定就是语法错误的
        if (isFormEncoded) {
          throw methodError(method, "Only one encoding annotation is allowed.");
        }
        //设置多表单提交标识
        isMultipart = true;
      } else if (annotation instanceof FormUrlEncoded) {
        //如果该方法有两个注解,前一个是多表单,后面又来一个单表单,那肯定就是语法错误的
        if (isMultipart) {
          throw methodError(method, "Only one encoding annotation is allowed.");
        }
        //设置单表单提交标识
        isFormEncoded = true;
      }
    }

而parseHttpMethodAndPath方法做的逻辑就简单:
(1)保存传入的"http请求方式"作为RequestFactory的成员变量。
(2)保存传入的是否有请求体hasBody标识作为RequestFactory的成员变量
(3)保存传入的相对路径relativeUrl作为RequestFactory的成员变量。
(4)判断了相对路径relativeUrl的写法,如果相对路径是这样写:@GET("/login?userName={name}"),GET注解中相对路径存在"?参数名={参数占位符}"这种写法,那么就抛出异常。强制用户改成:@GET("/login") login(@Query(userName) String name); 依靠@Query的方式给GET添加参数。
(5)允许的相对路径写法:“/{路径占位符}/info”或“/info/{路径占位符}”或者"/路径/路径"。而且会解析路径占位符,等后续路径值传递过来时能够进行替换。但是只会替换一个路径占位符。例如:“/{路径占位符1}/{路径占位符2}/info”,只会第一个遇到的占位符:路径占位符1。

  private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
      this.httpMethod = httpMethod;
      this.hasBody = hasBody;
      if (value.isEmpty()) {
        return;
      }
      //相对路径不允许存在这样的写法:/login?参数名={参数占位符}
      int question = value.indexOf('?');
      if (question != -1 && question < value.length() - 1) {
        // Ensure the query string does not have any named parameters.
        String queryParams = value.substring(question + 1);
        Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
        if (queryParamMatcher.find()) { throw methodError(method,
              "URL query string \"%s\" must not have replace block. "
                  + "For dynamic query parameters use @Query.",queryParams);
        }
      }
      //保存相对路径
      this.relativeUrl = value;
     //相对路径允许存在这样的写法:“/{路径占位符}/info”或“/info/{路径占位符}”
     //解析遇到的第一个"{路径占位符}",把它保存起来,后面赋值的时候,根据这里保存的路径占位符去验证:是否是给relativeUrlParamNames所指定的路径占位符赋值
      this.relativeUrlParamNames= parsePathParameters(value);
    }

方法上的注解解析完成之后,我们看看方法参数上的解析是怎样的。

//对于方法参数上的解析逻辑,了解每个参数是非常重要的,不了解方法参数根本看不懂
//int p-->当前方法参数的索引;
//Type parameterType-->当前方法参数的类型。
//Annotation[] annotations-->当前修饰该参数的所有的注解
//allowContinuation-->是否已经是最后一个参数了。
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判断的目的是:Retrofit不允许参数上有多个注解去修饰。java中是允许一个方法参数被多个注解修饰的,但是呢Retrofit不允许这样,一个方法参数只能搭配一个Retrofit的注解,多了Retrofit就抛异常:Multiple Retrofit annotations found, only one allowed"
          if (annotationAction == null) {
            continue;
          }
          if (result != null) {
            throw parameterError(  method, p, "Multiple Retrofit annotations found, only one allowed.");
          }
          //生成方法参数的处理类ParameterHandler对象。
          result = annotationAction;
        }
      }
    //这里只是对kotlin的挂起函数做支持而已。
      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;
    }

parseParameter()循环方法参数上的注解并进行解析,但它不是真正解析参数注解的方法,真正解析的地方被抽取到parseParameterAnnotation方法里面了。

parseParameterAnnotation()里面写了Retrofit中所有参数注解的解析逻辑,通过if代码块来分割开来,里面的代码是特别长度,但是因为它是解析方法参数上的注解,所以解析的过程基本上一致,我这里只分析@Url,@Path,@Query三个参数注解的解析逻辑,其他的逻辑是很相似的,我们可以用到的时候或者需要的时候才去看也行的。

//这里封装了所有方法参数注解的解析逻辑,我只选取其中@Url,@Path,@Query三个注解的解析逻辑进行分析 
//对于方法参数上的解析逻辑,了解每个参数是非常重要的,不了解方法参数根本看不懂
//int p-->当前方法参数的索引;
//Type parameterType-->当前方法参数的类型。
//Annotation[] annotations-->当前修饰该参数的所有的注解
private ParameterHandler<?> parseParameterAnnotation(
        int p, Type type, Annotation[] annotations, Annotation annotation) {
    //判断参数上的注解是否是@Url注解
      if (annotation instanceof Url) {
        //对参数类型检查,基本不需要理会,只要参数的泛型不是这两种:TypeVariable,写法如:TestReflect<T> ;WildcardType,写法如:List<? extends TestReflect> 就行了。
        validateResolvableType(p, type);
        //如果这个为true说明存在两个以上的@Url注解,那么抛出异常。为什么会这样判断呢?因为当前的parseParameterAnnotation方法是在循环方法参数的注解数组的过程中执行的!而注解数组中注解的顺序是在源文件中该参数上进行注解声明的顺序。例如:你在方法参数上贴的第一个注解是@Url那么反射获取的注解数组中第一个元素也是@Url。
        if (gotUrl) {
          throw parameterError(method, p, "Multiple @Url method annotations found.");
        }
       //方法参数上如果存在@Path注解,@Path不能与@Url一起使用。则抛出异常
        if (gotPath) {
          throw parameterError(method, p, "@Path parameters may not be used with @Url.");
        }
        //方法参数上如果存在@Query注解,@Query不能声明在@Url前面,要声明在@Url后面
        if (gotQuery) {
          throw parameterError(method, p, "A @Url parameter must not come after a @Query.");
        }
   //方法参数上如果存在@QueryName注解,@QueryName不能声明在@Url前面,要声明在@Url后面
        if (gotQueryName) {
          throw parameterError(method, p, "A @Url parameter must not come after a @QueryName.");
        }
  //方法参数上如果存在@QueryMap注解,@QueryMap不能声明在@Url前面,要声明在@Url后面
        if (gotQueryMap) {
          throw parameterError(method, p, "A @Url parameter must not come after a @QueryMap.");
        }
  //如果Http请求方法注解(例如:@GET,@POST,@DELETE等等),请求方式注解如果已经声明了相对路径,那么就不能在使用@Url注解来声明相对路径了。
        if (relativeUrl != null) {
          throw parameterError(method, p, "@Url cannot be used with @%s URL", httpMethod);
        }
       //上面的判断都没报错的话,说明该注解的使用是正确的,记录一下标识。
        gotUrl = true;
       //满足条件则创建ParameterHandler.RelativeUrl对象返回
        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.");
        }
      } else if (annotation instanceof Path) {
        validateResolvableType(p, type);
       //方法参数上如果存在@Query注解,@Path不能在@Query后面
        if (gotQuery) {
          throw parameterError(method, p, "A @Path parameter must not come after a @Query.");
        }
      //方法参数上如果存在@QueryName注解,@Path不能在@QueryName后面
        if (gotQueryName) {
          throw parameterError(method, p, "A @Path parameter must not come after a @QueryName.");
        }
       //方法参数上如果存在@QueryName注解,@Path不能在@QueryName后面
        if (gotQueryMap) {
          throw parameterError(method, p, "A @Path parameter must not come after a @QueryMap.");
        }
       //方法参数上如果存在@Url,@Path不能与@Url一起使用
        if (gotUrl) {
          throw parameterError(method, p, "@Path parameters may not be used with @Url.");
        }
       //Http请求方法注解(例如:@GET,@POST,@DELETE等等),请求方式注解如果没有声明相对路径,则抛出异常
        if (relativeUrl == null) {
          throw parameterError(
              method, p, "@Path can only be used with relative url on @%s", httpMethod);
        }
        //上面的判断都没报错的话,说明该注解的使用是正确的,记录一下标识。 
        gotPath = true;
        Path path = (Path) annotation;
        //从@Path注解中获取相对路径上"路径占位符"的名称
        String name = path.value();
        //在进行方法上的注解解析时让relativeUrlParamNames保存了指定的路径占位符,只能给relativeUrlParamNames指定的"路径占位符"赋值,否在抛出异常
        validatePathName(p, name);
       //从Retrofit中查找是否有处理该注解的Converter,默认是没有的配置,则返回一个ToStringConverter,该类只是调用一下toString方法就完事了。 
        Converter<?, String> converter = retrofit.stringConverter(type, annotations);
        //创建ParameterHandler.Path对象。
        return new ParameterHandler.Path<>(method, p, name, converter, path.encoded());
      } else if (annotation instanceof Query) {
        validateResolvableType(p, type);
        Query query = (Query) annotation;
        //获取@Query中value的值,该值将作为"路径1/路径?key=value"中的key存在。
        String name = query.value();
        boolean encoded = query.encoded();
        //获取参数的具体类型。
        Class<?> rawParameterType = Utils.getRawType(type);
        //记录一下@Query注解 已经处理的标识。
        gotQuery = true;
        //根据参数的类型进行不同的value转换,通常我们是走最后一个逻辑
        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);
          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 {
          //@Query最后只会走这里,仍然是通过ToStringConverter转换value值,然后创建ParameterHandler.Query对象并返回
          Converter<?, String> converter = retrofit.stringConverter(type, annotations);
          return new ParameterHandler.Query<>(name, converter, encoded);
        }
        ....其他剩余的类似代码块省略不贴了,因为都是对参数注解的解析而已,大同小异,上面这三个注解分析完之后其他的要看懂简直搓搓有余.....
      } 

上面分析了Retrofit是如何解析方法参数上的注解的,但是对于方法参数的注解生成的ParameterHandler类并没有进行讲解,我们也快速看看代码就能清楚,ParameterHandler做的事情:

对于 ParameterHandler.RelativeUrl类关键的转换方法如下:
 @Override
 void apply(RequestBuilder builder, @Nullable Object value) {
      if (value == null) {
        throw Utils.parameterError(method, p, "@Url parameter is null.");
      }
    //外界调用动态代理对象执行API接口方法时,传给该AIP接口方法的方法参数会层层传递进来,其中传给@Url注解的相对路径,会保存为RequestBuilder的成员变量,当RequestBuilder通过create()方法创建okhttp的reqeust对象时,这个相对路径RelativeUrl会与baseUrl一起拼接成完整的请求路径。
    builder.setRelativeUrl(value);
 }
----------------------------------------
对于ParameterHandler.Path类的关键代码如下:
@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.");
      }
   //外界调用动态代理对象执行API接口方法时,传给该AIP接口方法的方法参数会层层传递进来,其中用于给@Path注解替换相对路径中路径占位符的参数值,将会在addPathParam()内直接被替换到相对路径里面。
   //value是要替换掉路径占位符的具体路径。valueConverter在解析@Path这个模块中,默认传入的converter是ToStringConverter,ToStringConverter只会执行value.toString而已。encoded只是标识当前的值是否已经进行了URLEncode编码而已。如果编码过就不需要再次编码了
   builder.addPathParam(name, valueConverter.convert(value), encoded);
}
void addPathParam(String name, String value, boolean encoded) {
    if (relativeUrl == null) {
      throw new AssertionError();
    }
    ...省略部分代码....
    //直接替换相对路径relativeUrl中关于占位符部分的字符串,从而生成新的相对路径
    String newRelativeUrl = relativeUrl.replace("{" + name + "}", value);
    //保存新的相对路径
    relativeUrl = newRelativeUrl;
  }
-------------------------------------
对于对于ParameterHandler.Query类的关键代码如下:
@Override
void apply(RequestBuilder builder, @Nullable T value) throws IOException {
  if (value == null) return;
  //valueConverter在解析@Query这个模块中,默认传入的converter是ToStringConverter,ToStringConverter只会执行value.toString而已。
 //encoded只是标识当前的值是否已经进行了URLEncode编码而已。如果编码过就不需要再次编码了
  String queryValue = valueConverter.convert(value);
  if (queryValue == null) return;
  //简单理解:把键值对拼接在相对路径,形成"?&key=value"的形式存在。
  builder.addQueryParam(name, queryValue, encoded);
}

好了到此,RequestFactory的build()对API接口的解析和接口上方法参数的解析已经完成,将所有的解析结果保存在RequestFactory对象中,并返回了RequestFactory对象出去。

我们优先看看RequestFactory创建具体的okhttp.request对象的流程,这样RequestFactory就很清晰了。

RequestFactory.create(args)就是创建okhttp3.Request的最终方法。直接调用即可。

//参数Object[] args-->这个就是用户调用API方法时,给该API方法传递的参数。API方法参数按照方法的参数声明的顺序原封不动,层层传递到RequestFactory.create()中。
okhttp3.Request create(Object[] args) throws IOException {
    @SuppressWarnings("unchecked") 
    //因为在RequestFactory.build()过程中已经对API方法的每个参数已经生成了ParameterHandler对象了,每个参数都有自己的ParameterHandler为其赋值。
    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
    int argumentCount = args.length;
    //有多少参数就有多少ParameterHandler,如果两者的数量不对等,那就说明要么解析API的时候有某些方法参数没有正确生成ParameterHandler;要么就是参数传递过多了。总之就代表问题出现了。参数与ParameterHandler是一对一的。
    if (argumentCount != handlers.length) {
      throw new IllegalArgumentException( "Argument count (" + argumentCount
              + ") doesn't match expected count (" + handlers.length + ")");
    }
    //创建RequestBuilder并赋值,这些值都在RequestFactory.build()过程已经创建好了
    RequestBuilder requestBuilder =
        new RequestBuilder(httpMethod,baseUrl,relativeUrl,headers,contentType,
            hasBody,isFormEncoded,isMultipart);
    //对挂起函数的处理
    if (isKotlinSuspendFunction) {
      argumentCount--;
    }
    //记录一下参数值,用于打tag
    List<Object> argumentList = new ArrayList<>(argumentCount);
    for (int p = 0; p < argumentCount; p++) {
      argumentList.add(args[p]);
      //因为args是按照方法参数声明顺序的,而parameterHandlers也是按照方法参数声明顺序的,因此它们索引相同,能够正确把参数传递到对应的parameterHandler中。
      handlers[p].apply(requestBuilder, args[p]);//每个ParameterHandler的apply()逻辑在上面已经解析过了。
    }
    //处理好参数赋值逻辑后,即可通过build()创建okhttp.request()对象了
    return requestBuilder.get().tag(Invocation.class, new Invocation(method, argumentList)).build();
  }

那么我们的ServiceMethod.parseAnnotations()中第一个主要方法RequestFactory.parseAnnotations(retrofit, method);成功分析完了,也知道该方法是如何返回RequestFactory对象过来的了,那么就开始分析HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory)这个第二重要方法的流程。

HttpServiceMethod.parseAnnotations()流程解析

/*该方法只有三个重点方法:createCallAdapt(),createResponseConverter和new CallAdapted(构造方法参数)*/
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    //记录一下是否是挂起函数
    boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
    boolean continuationWantsResponse = false;
    boolean continuationBodyNullable = false;
    //获取方法上的接口注解。
    Annotation[] annotations = method.getAnnotations();
    Type adapterType;
    //如何是挂起函数就执行里面的逻辑
    if (isKotlinSuspendFunction) {
      //获取方法上返回值的类型,获取的返回值类型是带泛型参数的。
      Type[] parameterTypes = method.getGenericParameterTypes();
      //从“带泛型参数的返回值类型”中获取返回值类型中具体的泛型类型
      Type responseType =
          Utils.getParameterLowerBound(
              0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
     //如果返回值是类似这种:Call<Response<UserInfo>>的类型话,上面只是获取到Response<UserInfo>而已,这个if判断里面是想继续获取UserInfo这个具体的泛型形象。
        if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
        // Unwrap the actual body type from Response<T>.
        responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
        continuationWantsResponse = true;
      } else {//这里是空的,源码就是空的 }
    //重新生成一个返回类型
      adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
    //重新生成一个SkipCallbackExecutor注解。即便用户可能根本没贴这个注解。
      annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
    } else {
      //如果不是挂起函数,那么就直接获取"带泛型参数的返回值类型"
      adapterType = method.getGenericReturnType();
    }
   //重点1:创建CallAdapter。CallAdapter是用于后面对创建的OkhttpCall进行包装转换的适配器来的。     
   CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
    //获取转换后的Call对象究竟是想给用户返回怎样的数据类型    
    Type responseType = callAdapter.responseType();
    //这两个if判断就很容易理解啦:如果转换后的call想给用户返回这两个类型,就抛异常。
    if (responseType == okhttp3.Response.class) {
      throw methodError( method, "'"  + getRawType(responseType).getName()
       + "' is not a valid response body type. Did you mean ResponseBody?");
    }
    if (responseType == Response.class) {
      throw methodError(method, "Response must include generic type (e.g., Response<String>)");
    }
   //如果API接口方法本身没有返回值,那转换后的Call,返回类型就必须是Void
    if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
      throw methodError(method, "HEAD method must use Void as response type.");
    }
//重点2:创建ResponseConverter。用于对okhttp请求回来的数据进行解析,例如json解析。
    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);
    //这个callFactory,就是okhttpClient。我们给Retrofit赋值一个OkhttpClient也是直接交给该callFactory字段保存的,还不如直接叫okhttpclient字段呢,换一个名字就很不好理解哈哈。
    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    if (!isKotlinSuspendFunction) {
    //重点3:创建了CallAdapted对象,该类实现了HttpServiceMethod。这就是一切的开始,前面都是为它做的铺垫而已。
      return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    } else if (continuationWantsResponse) {
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
      return (HttpServiceMethod<ResponseT, ReturnT>)
          new SuspendForResponse<>(
              requestFactory,
              callFactory,
              responseConverter,
              (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
    } else {
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
      return (HttpServiceMethod<ResponseT, ReturnT>)
          new SuspendForBody<>(
              requestFactory,
              callFactory,
              responseConverter,
              (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
              continuationBodyNullable);
    }
  }
​

根据上面的HttpServiceMethod.parseAnnotations()的源码分析,我们发现最后是返回了CallAdapted对象,那么现在我们再次整理一下思路:动态代理实现了API接口的方法,用户通过动态代理调用API接口的方法时,内部实际上是调用InvocationHandler的invoke(API方法参数)方法,而InvocationHandler的invoke()则通过LoadServiceMethod()方法获取了一个ServiceMethod对象,并调用该ServiceMethod对象的Invoke()。而现在我们分析ServiceMethod的创建方法发现,实际上返回的是HttpServiceMethod的子类CallAdapted的实例对象,则CallAdapted的Invoke()方法必然返回了一个“与API接口中声明的返回值类型相同的对象”。例如:API接口声明的返回值是:Call<User>,那么CallAdapted的Invoke()方法必然返回了一个Call<User>。

abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {
  //HttpServiceMethod实现了父类ServiceMethod的Invoke方法,并且HttpServiceMethod自己还创建了一个抽象方法adapt。
  @Override
  final @Nullable ReturnT invoke(Object[] args) {
    //直接创建了真正的能够进行网络请求的OkHttpCall对象,其实如果我们把这个OkHttpCall对象直接返回作为API接口的返回值给到用户也是可以的,用户是能够拿着这个OkHttpCall对象进行网络请求,进行同步或者异步调用。但是~这样就写死了,返回值永远是Call对象了,所以子类HttpServiceMethod增加了抽象方法adapt(),对Call对象进行包装。例如API接口希望最终返回的是一个Rxjava的对象进行网络请求,那么就在这个adpat中实现“把Call对象包装成Rxjava对象的逻辑”就行了。
    Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    //包装Call对象,如何包装可以自定义处理。
    return adapt(call, args);
  }
 //用于对Call方法进行转换和包装
  protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);
//CallAdapted实现了HttpServiceMethod的adapt方法。但是CallAdapted并没有写死一个包装Call对象的逻辑,而且API接口需要的返回值类型也许是Call,也许是Observerble,也许是Single等等五花八门,所以这就需要交给工厂方法去做处理了。想要返回怎样的对象,循环每个工厂方法,工厂方法根据对比API接口Method对象的返回值类型确定是不是自己能创建的类型,是就执行并返回即可
 static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {
    private final CallAdapter<ResponseT, ReturnT> callAdapter;
​
    CallAdapted(
        RequestFactory requestFactory,
        okhttp3.Call.Factory callFactory,
        Converter<ResponseBody, ResponseT> responseConverter,
        CallAdapter<ResponseT, ReturnT> callAdapter) {
      super(requestFactory, callFactory, responseConverter);
      this.callAdapter = callAdapter;
    }
​
    //所以这里直接调用了一个工厂方法转换Call。
    @Override
    protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
      return callAdapter.adapt(call);
    }
  }
}

根据上面的分析,最终转换Call对象是交给工厂类去执行的,工厂类根据API的返回值确定自身是否能返回需要的实例实例,能返回就执行,不能就换下一个工厂类,这些工厂类都是CallAdapter接口的实现类。那么CallAdapter的实现类在哪里赋值的?在创建CallAdapted对象的时候就传递就来了。

  //重点1:创建CallAdapter。CallAdapter是用于后面对创建的OkhttpCall进行包装转换的适配器来的。     
   CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);

createCallAdapter()内部是只有一行:调用 retrofit.callAdapter();而retrofit.callAdapter();内部也只有一行:调用retrofit.nextCallAdapter();

/*CallAdapter.Factory skipPast-->为null
Type returnType-->API接口方法的返回值类型,是"带泛型参数的返回值类型"
Annotation[] annotations-->API接口的方法上的注解
*/
public CallAdapter<?, ?> nextCallAdapter(
      @Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) {
    Objects.requireNonNull(returnType, "returnType == null");
    Objects.requireNonNull(annotations, "annotations == null");
    //这行的意思是:callAdapterFactories这个List里面如果由很多Factory对象的话,你想跳过哪一些Factory,从跳过之后的Factory开始进行查找list集合。
    int start = callAdapterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
    //每个Factory都会根据返回值类型判断是否是自己能个提供的数据类型,不是就返回null。是就提供对象实例。 
    CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }
//后面这些是 如果上面的集合遍历结束 说明没有能提供CallAdapter的工厂类,那就抛出异常。
    StringBuilder builder =
        new StringBuilder("Could not locate call adapter for ").append(returnType).append(".\n");
    if (skipPast != null) {
      builder.append("  Skipped:");
      for (int i = 0; i < start; i++) {
        builder.append("\n   * ").append(callAdapterFactories.get(i).getClass().getName());
      }
      builder.append('\n');
    }
    builder.append("  Tried:");
    for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
      builder.append("\n   * ").append(callAdapterFactories.get(i).getClass().getName());
    }
    throw new IllegalArgumentException(builder.toString());
  }
​

nextCallAdapter代码中的callAdapterFactories集合是在哪里赋值的呢?

//该方法是在Retrofit.Build()中对外提供了方法,用户可以进行添加。
 public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
      callAdapterFactories.add(Objects.requireNonNull(factory, "factory == null"));
      return this;
}

另外createResponseConverter()方法与createCallAdapter()方法的模式一模一样。只不过获取的集合变了而已,连获取的方式都一模一样。

//重点2:创建ResponseConverter。用于对okhttp请求回来的数据进行解析,例如json解析。
    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);
-------------------------
public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
      @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
    Objects.requireNonNull(type, "type == null");
    Objects.requireNonNull(annotations, "annotations == null");
​
    int start = converterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = converterFactories.size(); i < count; i++) {
       //循环转换器,转换器会根据类型去判断是否支持转换,不支持的返回null。
      Converter<ResponseBody, ?> converter =
          converterFactories.get(i).responseBodyConverter(type, annotations, this);
      if (converter != null) {
        return (Converter<ResponseBody, T>) converter;
      }
    }
    ...省略了抛出异常的代码,跟createCallAdapter()一模一样哈...
    throw new IllegalArgumentException(builder.toString());
  }  
--------------------
//对外提供了添加工厂的方法。
public Builder addConverterFactory(Converter.Factory factory) {
      converterFactories.add(Objects.requireNonNull(factory, "factory == null"));
      return this;
}

就算用户没添加CallAdapter.Factory,或者没添加Converter.Factoru,还可以通过Retrofit.Build创建Retrofit对象时默认会添加相关工厂类。

//一开始的让用户创建Retrofit的方法
public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }
    //如果设置了okhttpClient就直接使用,否在使用默认的okhttClient。
      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }
    //用于给okhttp请求后台之后,获取到的网络数据后,能个切线程去处理数据
      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        //默认创建一个切到Android主线程的执行器。内部就简单的一行代码通过handler.post(Runnable)把Callback回调放在主线程。
        callbackExecutor = platform.defaultCallbackExecutor();
      }
    
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
    //添加默认的CallAdapter。
    callAdapterFactories.addAll(
        platform.defaultCallAdapterFactories(callbackExecutor));
    //创建数据解析器的集合
      List<Converter.Factory> converterFactories  new ArrayList<>(
              1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
    //各种数据解析器。Retrofit的默认解析器我们都用不上,通常我们要自己加一个Gson解析器
      converterFactories.add(new BuiltInConverters());
      converterFactories.addAll(this.converterFactories);
      converterFactories.addAll(platform.defaultConverterFactories());
   return new Retrofit( 
       callFactory, baseUrl,
       unmodifiableList(converterFactories),                                            unmodifiableList(callAdapterFactories),
       callbackExecutor, validateEagerly);
    }
}

retrofit的分析基本结束。

上一篇:java-使用Retrofit发送POST请求时无法获取POST参数


下一篇:ASP.NET Core 处理 404 Not Found