Retrofit解析9之流程解析

整体Retrofit内容如下:

本片文章的主要内容如下:

  • 1、整体流程简介
  • 2、流程详解
  • 3、总结
  • 4、okHttp+Retrofit的整体架构

一、Retrofit整体流程简介

其实整个Retrofit的流程如下图:


retrofit.png

PS:该图不是我画的,网上盗来的

  • 这张图完美的诠释了Retrofit的整个流程图
  • 首选创建Retrofit,配置响应的参数,然后Retrofit会的的请求都会交给OkHttp去执行,执行的结果返回Response,再根据转换器进行解析成相对应的返回值类型T。Retrofit内部使用了动态代理,方便了使用,通过retrofit.create返回的其实是一个动态代理类,所有具体的逻辑处理交给ServiceMethod来进行处理。
如果抛各种开适配器和Converter的情况,只考虑发起请求,我们可以这样理解的流程如下图:
单一流程.png

Retrofit总体使用的是外观模式,Retrofit持有所有子系统的引用;Retrofit有两个比较重要的两个Factory,一个是用来生成对应"Call"的CallAdapter的CallAdapterFactory;一个是用来进行响应数据转化(反序列化)的ConvertFactory;这两个都可以用户自己进行添加。在自定义的Service中,每一个method一一对应一个ServiceMethod,而ServiceMethod持有一个Retrofit,前面两个Factory以及生成的Request的RequestBuilder;在okHttp中,Request需要自己进行定义创建,而Retrofit简化了这个操作,进行相应的封装,使用注解的方式来定义RequestBuilder相关参数信息;注解信息的解析则在parseMethodAnnotation()方法里面和parseParameterAnnotation()方法里面分别解析方法注解和参数注解。最终通过RequestBuilder来具体陈生一个Request,RequestBuilder中持有okHttp中的Request.Builder类的引用,其创建Request的过程其实都是交给okHttp来操作的;生成的Request最终封装成一个OkHttpCall,OkHttpCall则可以看做是对okHttp中Call的代理,同时对okHttp的返回Response进行解析,使用convertFactory将其解析为用户所期望的返回类型。

二、流程详解

我们讲解Retrofit整体流程,就依据官方给的demo来吧,代码如下:
代码如下:

public interface GitHub {
    @GET("/repos/{owner}/{repo}/contributors")
    Call<List<Contributor>> contributors(
        @Path("owner") String owner,
        @Path("repo") String repo);
  }

  public static void main(String... args) throws IOException {
    Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(API_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build();
    GitHub github = retrofit.create(GitHub.class);
    Call<List<Contributor>> call = github.contributors("square", "retrofit");
    List<Contributor> contributors = call.execute().body();
    for (Contributor contributor : contributors) {
      System.out.println(contributor.login + " (" + contributor.contributions + ")");
    }
  }

这就是Retrofit最简单的用法,可以从代码看出:

  • 首先定义一个接口,接口中的方法用注解方式声明了HTTP请求的相关参数,包括GET方法,相关参数等。方法的返回值为Call<List<Contributor>>,其中Contributor是定义的一个JavaBean类,即业务所需要的数据格式。
  • 其次实例化了一个Retrofit对象,用Retrofit的builder,指定了baseUrl,指定了ConverterFactory,即表示用Gson解析返回值来得到JavaBean。
  • 用retrofit.create(GitHub.class)方法得到了一个GitHub实例对象(框架动态代理的方法帮我们生成了接口的实例,后续详细说)

(3).用retrofit.create(GitHub.class)方法得到了GitHub实例对象(框架用动态代理的方式帮我们生成了接口的实例,后续详细说),调用对象方法得到call对象。其中,call对象有excute()和enqueue()方法,分别为同步和异步进行网络请求。

所以我们把整个流程分解为5大部分

  • 1、Retrofit初始化
  • 2、发起网络请求
  • 3、执行请求
  • 4、处理响应

(一)、Retrofit初始化

具体代码如下:

        .baseUrl(API_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build();

Retrofit的构建,其实就是一个简单的建造者模式(Builder),通过Builder来进行构建。如下图所示:

Retrofit.Builder.png

其中Retrofit.Builder有以下参数可以设置:

  • okhttp3.Call.Factory callFactory。
  • HttpUrl baseUrl。
  • List<Converter.Factory> converterFactories。
  • List<CallAdapter.Factory> adapterFactories。
  • Executor callbackExecutor。
  • boolean validateEagerly。

补充说明:

  • 由于是调用无参的Retrofit.Builder()的构造函数,而无参的构造函数内部又调用了 this(Platform.get());在 this(Platform.get())里面有调用了 converterFactories.add(new BuiltInConverters()),所以Builder的里面默认带一个BuiltInConverters的实例。
  • 以上参数均可以通过Retrofit.Builder的addXXX方法进行添加,如果大家对上面的变量理解有疑问,请看上篇文章。

下面我们重点说下build()方法的内容:
代码如下;

 public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }
      // 创建OkHttp,目前Retrofit只支持OkHttp
      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }
      // 创建Executor
      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }
     // 对adapters进行保护性拷贝,并且加入默认的call adapter(使用上面创建的Executor来构建,可以认为是把主线程中的Handler传入, 构建adapterFactories集合,将defaultAdapterFactory加入其中
      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
  adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
      // 对converters进行保护性拷贝,一般传入的为GsonConverterFactory对象,其作用主要是将json转换成java对象
      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
       //构建Retrofit对象
      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
    }

build方法主要思路:

  • 1、创建callFactory对象
  • 2、创建callbackExecutor对象,有平台可知,其实就是主线程,通过MainLooper来构建Handler,其execute方法本质是Handler.post(runnable),主要用于线程切换
  • 3、创建adapterFactories集合,同时添加一个默认的adapterFactory,即通过调用platform.defaultCallAdapterFactory(callbackExecutor),其实就是ExecutorCallAdapterFactory类实例,这样保证adapterFactories里面至少有一个adapterFactory。而ExecutorCallAdapterFactory的get(Type,Annotation[], Retrofit)方法里面返回的CallAdapter其实是一个new的一个匿名内部类,而这个匿名内部类的里面的adapt(Call<Object>)方法返回的是一个ExecutorCallbackCall对象,而ExecutorCallbackCall对象内部又持有retrofit2.Call对象,如果大家之前看过我前面写的文章知道,retrofit2.Call仅仅是接口,retrofit2.Call的实现类是OkHttpCall,而OkHttpCall里面又吃有一个okhttp3.Call,真正发起请求的就是这个okhttp3.Call,这样ExecutorCallbackCall就间接持有了okhttp3.Call,其实OkHttpCall就是okhttp3.Call的包装类。
  • 4、创建一个converterFactories的集合,并把自己的converterFactories添加进去,这个converterFactories在调用无参的构造函数时进行初始化的。

PS:
ExecutorCallbackCall类里面有个变量叫做final retrofit2.Call<T> delegate,而这个retrofit2.Call的具体实现类是retrofit2.OkHttpCall,在OkHttpCall类内部有一个变量就叫做 private okhttp3.Call rawCall;这样ExecutorCallbackCall就间接持有一个okhttp3.Call的对象了,如下图:


ExecutorCallbackCall.png

(二)、发起网络请求

1、定义Api接口:

    @GET("/repos/{owner}/{repo}/contributors")
    Call<List<Contributor>> contributors(@Path("owner") String owner, @Path("repo") String repo);

关于两个注解:@GET 和 @Path,我这里就不详细说明了,不懂的看我前面的文章。

2、获取一个请求操作:

    GitHub github = retrofit.create(GitHub.class);

调用retrofit.create(GitHubService.class)我们就可以获取一个实例了
这里面我们详细分析下:

3、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 (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);
          }
        });
  }

流程图如下:


流程.png
(1)说下整体流程成,运用动态代理技术获取了一个GitHubService的一个实例。

这里面运用了动态代理技术,动态代理方法Proxy.newProxyInstance返回的便是该接口的代理对象。invoke方法为接口方法的具体实现,invoke()方法里面的method为具体的方法(在demo中为contributors方法);args是该方法的参数(在demo中为new String[]{"square", "retrofit"})。

动态代理模型如下:


动态代理.png

如果大家对这块技术不是很熟悉请看我上一篇文章。

(2)说下eagerlyValidateMethods()方法

大家看下这块代码:

    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
  private void eagerlyValidateMethods(Class<?> service) {
    Platform platform = Platform.get();
    for (Method method : service.getDeclaredMethods()) {
      if (!platform.isDefaultMethod(method)) {
        loadServiceMethod(method);
      }
    }
  }

所以如果我们在Retrofit.Builder的validateEagerly参数被设置为true,我们在create方法执行的时候,就会遍历这个service的所有方法,由于platform.isDefaultMethod(method)在Android平台默认返回false,所以一定会执行 loadServiceMethod(method),也就是说如果设置validateEagerly=true,则Retrofit会把这个service接口的所有方法都load进来,也就说一次性全部导入。

(3)说下loadServiceMethod()方法
  ServiceMethod<?, ?> loadServiceMethod(Method method) {
    ServiceMethod<?, ?> result = serviceMethodCache.get(method);
    if (result != null) return result;
    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = new ServiceMethod.Builder<>(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }
  • serviceMethodCache是一个一级缓存。使用缓存的目的是,由于反射比较消耗性能,所以为了避免这种性能消耗,我们保证同一个接口(api)的同一个方法,只会创建一次,由于我们每次获取的接口(api)实例都是传入的class对象,而class对象是在进程内单例的,所以获取她的同一个方法Method实例也是单利的,所以这个缓存是有效的。
  • loadServiceMethod()方法,通过代码我们知道,结合了一级缓存serviceMethodCache,如果缓存里面有,则直接从缓存里面拿,如果缓存没有,则new一个ServiceMethod.Builder对象,然后调用它的build()方法来获取一个ServiceMethod,把它放入serviceMethodCache中,并返回。
(4)、new ServiceMethod.Builder<>(this, method)方法详解:
    Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      this.methodAnnotations = method.getAnnotations();
      this.parameterTypes = method.getGenericParameterTypes();
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }

通过这个ServiceMethod.Builder的构造函数, ServiceMethod.Builder这个实例就获取了Retrofit对象和method对象,通过对method的解析,又获取了方法的注解,该方法参数的类型,该方法参数的注解。完成了ServiceMethod.Builder的初始化。

(5)、ServiceMethod.Builder<>().build()方法详解:
public ServiceMethod build() {
      callAdapter = createCallAdapter();
      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 = createResponseConverter();

      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }
      if (httpMethod == null) {
        throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
      }
      if (!hasBody) {
        if (isMultipart) {
          throw methodError(
              "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
        }
        if (isFormEncoded) {
          throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
              + "request body (e.g., @POST).");
        }
      }
      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);
      }
      if (relativeUrl == null && !gotUrl) {
        throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
      }
      if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
        throw methodError("Non-body HTTP method cannot contain @Body.");
      }
      if (isFormEncoded && !gotField) {
        throw methodError("Form-encoded method must contain at least one @Field.");
      }
      if (isMultipart && !gotPart) {
        throw methodError("Multipart method must contain at least one @Part.");
      }
      return new ServiceMethod<>(this);
    }

这个方法里面我们重点讲解下几个部分

第一部分-----方法createCallAdapter()解析

createCallAdapter主要是获取对应的CallAdapter的对象,那我们就详细分析下createCallAdapter()内部的代码。
看下代码

    private CallAdapter<T, R> createCallAdapter() {
      Type returnType = method.getGenericReturnType();
      if (Utils.hasUnresolvableType(returnType)) {
        throw methodError(
            "Method return type must not include a type variable or wildcard: %s", returnType);
      }
      if (returnType == void.class) {
        throw methodError("Service methods cannot return void.");
      }
      Annotation[] annotations = method.getAnnotations();
      try {
        return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
      } catch (RuntimeException e) { 
        throw methodError(e, "Unable to create call adapter for %s", returnType);
      }
    }

callAdapter 主要负责把retrofit2.Call<R> 转化为T (注意和okhttp3.Call 区分开来) ,retrofit2.Call<R> 表示的是对一个Retrofit的接口方法的调用,也就是我们举得例子 Call<List<Contributor>> call = github.contributors("square", "retrofit")。通过上述代码我们知道createCallAdapter的内部其实是通过retrofit类中的callAdapter()方法来实现的。那我们再来看下retrofit类中的callAdapter()方法:

  public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
  }
  public CallAdapter<?, ?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
    checkNotNull(returnType, "returnType == null");
    checkNotNull(annotations, "annotations == null");

    int start = adapterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = adapterFactories.size(); i < count; i++) {
      CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }

    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(adapterFactories.get(i).getClass().getName());
      }
      builder.append('\n');
    }
    builder.append("  Tried:");
    for (int i = start, count = adapterFactories.size(); i < count; i++) {
      builder.append("\n   * ").append(adapterFactories.get(i).getClass().getName());
    }
    throw new IllegalArgumentException(builder.toString());
  }

之前我们讲过,如果我们不addCallAdapterFactory()方法的话,那么在Retrofit.build()方法中,系统会添加一个平台默认的值:

 List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
 adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

也就是没有设置,那么这里应该会添加平台默认的CallAdapterFactory,其实是ExecutorCallAdapterFactory类,。那我们再回到nextCallAdapter()中,在nextCallAdapter()方法中我们遍历adapterFactories,调动每一个CallAdapter.Factory的get()方法来获取一个CallAdapter。说白了,就是根据注解和返回值的类型,判断,如果你这个CallAdapter支持则返回对应的CallAdapter,如果不支持则返回null。

第二部分-----createResponseConverter()方法解析
    private Converter<ResponseBody, T> createResponseConverter() {
      Annotation[] annotations = method.getAnnotations();
      try {
        return retrofit.responseBodyConverter(responseType, annotations);
      } catch (RuntimeException e) { 
        throw methodError(e, "Unable to create converter for %s", responseType);
      }
    }

Converter<ResponseBody, T>主要负责把okhttp3.ResponseBody转化为类型T的对象。通过代码我们知道其实是调用retrofit类中的responseBodyConverter()方法,那我们来看下responseBodyConverter()方法里面的具体实现

  public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
    return nextResponseBodyConverter(null, type, annotations);
  }

  public <T> Converter<ResponseBody, T> nextResponseBodyConverter(Converter.Factory skipPast,
      Type type, Annotation[] annotations) {
    checkNotNull(type, "type == null");
    checkNotNull(annotations, "annotations == null");
    int start = converterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = converterFactories.size(); i < count; i++) {
      Converter<ResponseBody, ?> converter =
          converterFactories.get(i).responseBodyConverter(type, annotations, this);
      if (converter != null) {
        return (Converter<ResponseBody, T>) converter;
      }
    }
    StringBuilder builder = new StringBuilder("Could not locate ResponseBody converter for ")
        .append(type)
        .append(".\n");
    if (skipPast != null) {
      builder.append("  Skipped:");
      for (int i = 0; i < start; i++) {
        builder.append("\n   * ").append(converterFactories.get(i).getClass().getName());
      }
      builder.append('\n');
    }
    builder.append("  Tried:");
    for (int i = start, count = converterFactories.size(); i < count; i++) {
      builder.append("\n   * ").append(converterFactories.get(i).getClass().getName());
    }
    throw new IllegalArgumentException(builder.toString());
  }

由于我们添加了

.addConverterFactory(GsonConverterFactory.create())

所以在Retrofit里面的converterFactories里面有两个Converter.Factory,一个是默认的BuiltInConverters,一个是GsonConverterFactory。
那我们再回到nextResponseBodyConverter()中,在nextResponseBodyConverter()方法中我们遍历converterFactories,调动每一个Converter.Factory的responseBodyConverter()方法来获取一个Converter。说白了,就是根据返回值类型、参数注解、方法注解来获取一个对应的Converter,如果你这个Converter.Factory支持则返回对应的Converter,则返回一个对应的Converter,如果不支持则返回null。在我们的demo中由于使用了GsonConverterFactory的get()方法返回的是GsonConverterFactory,而GsonConverterFactory的responseBodyConverter()返回到是GsonResponseBodyConverter实例。

第三部分-----方法注解解析

看代码

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

通过遍历该方法的所有方法注解,调用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);
      } else if (annotation instanceof Multipart) {
        if (isFormEncoded) {
          throw methodError("Only one encoding annotation is allowed.");
        }
        isMultipart = true;
      } else if (annotation instanceof FormUrlEncoded) {
        if (isMultipart) {
          throw methodError("Only one encoding annotation is allowed.");
        }
        isFormEncoded = true;
      }
    }
  • 1、通过解析方法的注解,我们来获取HTTP的请求的请求方法,
  • 2、然后调用parseHttpMethodAndPath()方法来设置Builder的relativeUrlrelativeUrlParamNames 两个字段的值。
  • 3、如果有设置请求头,则获取请求头的值,并添加到Builder的headers
  • 4、如果请求类型是Multipart的,则设置isMultipart =true
  • 5、如果请求类型是isFormEncoded的,则设置 isFormEncoded=true

Multipart大家不了解的看我前面的文章,这里面说下isMultipartisFormEncoded是互斥的,所以后面解析方法参数的时候,遇到一些错误的操作,会直接抛异常,比如你方法注解使用了@Multipart,但是在参数那里使用了注解Field,由于是互斥的,所以会抛异常。

所有通过方法注解解析(也就是parseMethodAnnotation()),我们完成了部分Builder的初始化。
第四部分-----内部检验
if (httpMethod == null) {
        throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
      }
      if (!hasBody) {
        if (isMultipart) {
          throw methodError(
              "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
        }
        if (isFormEncoded) {
          throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
              + "request body (e.g., @POST).");
        }
      }
  • 1、请求方式一定要有
  • 2、如果没有请求体,则一定不能使用@Multipart和@FormUrlEncoded
第五部分-----参数注解解析

代码如下:

      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);
      }
  • 1、首先获取注解的个数,由于注解的个数一定是和方法参数的个数一一对应的
  • 2、给Buidler的parameterHandlers赋值,仅仅是new了一个对象,每个元素的值还是null
  • 3、获取入参的类型,并判断,如果是不能处理的类型则直接抛异常
  • 4、然后调用parseParameter来解析方法参数注解

那让我们看下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) {
          continue;
        }
        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;
    }

parseParameter方法内部其实也是遍历这个参数的所有注解,然后通过调用
parseParameterAnnotation()方法来解析每一个注解。那么我们来看下
parseParameterAnnotation()方法内部的实现

private ParameterHandler<?> parseParameterAnnotation(
        int p, Type type, Annotation[] annotations, Annotation annotation) {
      if (annotation instanceof Url) {
        if (gotUrl) {
          throw parameterError(p, "Multiple @Url method annotations found.");
        }
        if (gotPath) {
          throw parameterError(p, "@Path parameters may not be used with @Url.");
        }
        if (gotQuery) {
          throw parameterError(p, "A @Url parameter must not come after a @Query");
        }
        if (relativeUrl != null) {
          throw parameterError(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();
        } 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) {
          throw parameterError(p, "A @Path parameter must not come after a @Query.");
        }
        if (gotUrl) {
          throw parameterError(p, "@Path parameters may not be used with @Url.");
        }
        if (relativeUrl == null) {
          throw parameterError(p, "@Path can only be used with relative url on @%s", httpMethod);
        }
        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 QueryName) {
        QueryName query = (QueryName) annotation;
        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.QueryName<>(converter, encoded).iterable();
        } else if (rawParameterType.isArray()) {
          Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
          Converter<?, String> converter =
              retrofit.stringConverter(arrayComponentType, annotations);
          return new ParameterHandler.QueryName<>(converter, encoded).array();
        } else {
          Converter<?, String> converter =
              retrofit.stringConverter(type, annotations);
          return new ParameterHandler.QueryName<>(converter, encoded);
        }
      } else if (annotation instanceof QueryMap) {
        Class<?> rawParameterType = Utils.getRawType(type);
        if (!Map.class.isAssignableFrom(rawParameterType)) {
          throw parameterError(p, "@QueryMap parameter type must be Map.");
        }
        Type mapType = Utils.getSupertype(type, rawParameterType, Map.class);
        if (!(mapType instanceof ParameterizedType)) {
          throw parameterError(p, "Map must include generic types (e.g., Map<String, String>)");
        }
        ParameterizedType parameterizedType = (ParameterizedType) mapType;
        Type keyType = Utils.getParameterUpperBound(0, parameterizedType);
        if (String.class != keyType) {
          throw parameterError(p, "@QueryMap keys must be of type String: " + keyType);
        }
        Type valueType = Utils.getParameterUpperBound(1, parameterizedType);
        Converter<?, String> valueConverter =
            retrofit.stringConverter(valueType, annotations);
        return new ParameterHandler.QueryMap<>(valueConverter, ((QueryMap) annotation).encoded());
      } else if (annotation instanceof Header) {
        Header header = (Header) annotation;
        String name = header.value();
        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.Header<>(name, converter).iterable();
        } else if (rawParameterType.isArray()) {
          Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
          Converter<?, String> converter =
              retrofit.stringConverter(arrayComponentType, annotations);
          return new ParameterHandler.Header<>(name, converter).array();
        } else {
          Converter<?, String> converter =
              retrofit.stringConverter(type, annotations);
          return new ParameterHandler.Header<>(name, converter);
        }
      } else if (annotation instanceof HeaderMap) {
        Class<?> rawParameterType = Utils.getRawType(type);
        if (!Map.class.isAssignableFrom(rawParameterType)) {
          throw parameterError(p, "@HeaderMap parameter type must be Map.");
        }
        Type mapType = Utils.getSupertype(type, rawParameterType, Map.class);
        if (!(mapType instanceof ParameterizedType)) {
          throw parameterError(p, "Map must include generic types (e.g., Map<String, String>)");
        }
        ParameterizedType parameterizedType = (ParameterizedType) mapType;
        Type keyType = Utils.getParameterUpperBound(0, parameterizedType);
        if (String.class != keyType) {
          throw parameterError(p, "@HeaderMap keys must be of type String: " + keyType);
        }
        Type valueType = Utils.getParameterUpperBound(1, parameterizedType);
        Converter<?, String> valueConverter =
            retrofit.stringConverter(valueType, annotations);
        return new ParameterHandler.HeaderMap<>(valueConverter);
      } 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 FieldMap) {
        if (!isFormEncoded) {
          throw parameterError(p, "@FieldMap parameters can only be used with form encoding.");
        }
        Class<?> rawParameterType = Utils.getRawType(type);
        if (!Map.class.isAssignableFrom(rawParameterType)) {
          throw parameterError(p, "@FieldMap parameter type must be Map.");
        }
        Type mapType = Utils.getSupertype(type, rawParameterType, Map.class);
        if (!(mapType instanceof ParameterizedType)) {
          throw parameterError(p,
              "Map must include generic types (e.g., Map<String, String>)");
        }
        ParameterizedType parameterizedType = (ParameterizedType) mapType;
        Type keyType = Utils.getParameterUpperBound(0, parameterizedType);
        if (String.class != keyType) {
          throw parameterError(p, "@FieldMap keys must be of type String: " + keyType);
        }
        Type valueType = Utils.getParameterUpperBound(1, parameterizedType);
        Converter<?, String> valueConverter =
            retrofit.stringConverter(valueType, annotations);
        gotField = true;
        return new ParameterHandler.FieldMap<>(valueConverter, ((FieldMap) annotation).encoded());
      } else if (annotation instanceof Part) {
        if (!isMultipart) {
          throw parameterError(p, "@Part parameters can only be used with multipart encoding.");
        }
        Part part = (Part) annotation;
        gotPart = true;
        String partName = part.value();
        Class<?> rawParameterType = Utils.getRawType(type);
        if (partName.isEmpty()) {
          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);
            if (!MultipartBody.Part.class.isAssignableFrom(Utils.getRawType(iterableType))) {
              throw parameterError(p,
                  "@Part annotation must supply a name or use MultipartBody.Part parameter type.");
            }
            return ParameterHandler.RawPart.INSTANCE.iterable();
          } else if (rawParameterType.isArray()) {
            Class<?> arrayComponentType = rawParameterType.getComponentType();
            if (!MultipartBody.Part.class.isAssignableFrom(arrayComponentType)) {
              throw parameterError(p,
                  "@Part annotation must supply a name or use MultipartBody.Part parameter type.");
            }
            return ParameterHandler.RawPart.INSTANCE.array();
          } else if (MultipartBody.Part.class.isAssignableFrom(rawParameterType)) {
            return ParameterHandler.RawPart.INSTANCE;
          } else {
            throw parameterError(p,
                "@Part annotation must supply a name or use MultipartBody.Part parameter type.");
          }
        } else {
          Headers headers =
              Headers.of("Content-Disposition", "form-data; name=\"" + partName + "\"",
                  "Content-Transfer-Encoding", part.encoding());

          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);
            if (MultipartBody.Part.class.isAssignableFrom(Utils.getRawType(iterableType))) {
              throw parameterError(p, "@Part parameters using the MultipartBody.Part must not "
                  + "include a part name in the annotation.");
            }
            Converter<?, RequestBody> converter =
                retrofit.requestBodyConverter(iterableType, annotations, methodAnnotations);
            return new ParameterHandler.Part<>(headers, converter).iterable();
          } else if (rawParameterType.isArray()) {
            Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
            if (MultipartBody.Part.class.isAssignableFrom(arrayComponentType)) {
              throw parameterError(p, "@Part parameters using the MultipartBody.Part must not "
                  + "include a part name in the annotation.");
            }
            Converter<?, RequestBody> converter =
                retrofit.requestBodyConverter(arrayComponentType, annotations, methodAnnotations);
            return new ParameterHandler.Part<>(headers, converter).array();
          } else if (MultipartBody.Part.class.isAssignableFrom(rawParameterType)) {
            throw parameterError(p, "@Part parameters using the MultipartBody.Part must not "
                + "include a part name in the annotation.");
          } else {
            Converter<?, RequestBody> converter =
                retrofit.requestBodyConverter(type, annotations, methodAnnotations);
            return new ParameterHandler.Part<>(headers, converter);
          }
        }

      } else if (annotation instanceof PartMap) {
        if (!isMultipart) {
          throw parameterError(p, "@PartMap parameters can only be used with multipart encoding.");
        }
        gotPart = true;
        Class<?> rawParameterType = Utils.getRawType(type);
        if (!Map.class.isAssignableFrom(rawParameterType)) {
          throw parameterError(p, "@PartMap parameter type must be Map.");
        }
        Type mapType = Utils.getSupertype(type, rawParameterType, Map.class);
        if (!(mapType instanceof ParameterizedType)) {
          throw parameterError(p, "Map must include generic types (e.g., Map<String, String>)");
        }
        ParameterizedType parameterizedType = (ParameterizedType) mapType;
        Type keyType = Utils.getParameterUpperBound(0, parameterizedType);
        if (String.class != keyType) {
          throw parameterError(p, "@PartMap keys must be of type String: " + keyType);
        }
        Type valueType = Utils.getParameterUpperBound(1, parameterizedType);
        if (MultipartBody.Part.class.isAssignableFrom(Utils.getRawType(valueType))) {
          throw parameterError(p, "@PartMap values cannot be MultipartBody.Part. "
              + "Use @Part List<Part> or a different value type instead.");
        }
        Converter<?, RequestBody> valueConverter =
            retrofit.requestBodyConverter(valueType, annotations, methodAnnotations);
        PartMap partMap = (PartMap) annotation;
        return new ParameterHandler.PartMap<>(valueConverter, partMap.encoding());
      } 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静态内部类对象之前,有一定的验证条件,保证内部逻辑清晰,不矛盾。

所有通过参数注解解析(也就是parseParameter()),我们完成了另一部分Builder的初始化,这样整个Builder的初始化就完成了,他对应的各个属性也赋值完毕。
第六部分-----排除错误逻辑
      if (relativeUrl == null && !gotUrl) {
        throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
      }
      if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
        throw methodError("Non-body HTTP method cannot contain @Body.");
      }
      if (isFormEncoded && !gotField) {
        throw methodError("Form-encoded method must contain at least one @Field.");
      }
      if (isMultipart && !gotPart) {
        throw methodError("Multipart method must contain at least one @Part.");
      }

主要是把互斥的几种情况添加进去,如果出现则违反了原则,直接抛异常

第七部分-----构造ServiceMethod
return new ServiceMethod<>(this);

这时候我们用这个Builder作为参数new了一个ServiceMethod对象,并返回

  ServiceMethod(Builder<R, T> builder) {
    this.callFactory = builder.retrofit.callFactory();
    this.callAdapter = builder.callAdapter;
    this.baseUrl = builder.retrofit.baseUrl();
    this.responseConverter = builder.responseConverter;
    this.httpMethod = builder.httpMethod;
    this.relativeUrl = builder.relativeUrl;
    this.headers = builder.headers;
    this.contentType = builder.contentType;
    this.hasBody = builder.hasBody;
    this.isFormEncoded = builder.isFormEncoded;
    this.isMultipart = builder.isMultipart;
    this.parameterHandlers = builder.parameterHandlers;
  }
在ServiceMethod的构造函数里面,我们完成了Builder里面的相应字段的值传递给ServiceMethod。 至此整个ServiceMethod.Builderd的build()方法执行完毕。

所以在ServiceMethod.Builder.build()方法里面主要是给Builder的各个属性赋值。保证这些属性最后正确地传递给ServiceMethod。在build()方法里面的流程如下:

  • 1、获取对应的CallAdatper
  • 2、获取对应的Converter
  • 3、方法注解解析,给相应的ServiceMethod.Builder的字段赋值,从注解转化为对象。
  • 4、内部检查,保证逻辑正确
  • 5、方法参数注解解析,给相应的ServiceMethod.Builder的字段赋值,从注解转化为对象。
  • 6、排除错误逻辑。(此时ServiceMethod.Builder的字段已经赋值完毕)
  • 7、构造ServiceMethod对象并返回

整个上面流程如下图:


流程图.png
(6)、Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)方法详解:
   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 (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);
          }
        });

大体流程如下:

  • 1、判断是不是Object的方法,如果是直接invoke
  • 2、根据平台来判断是否是默认的方法
  • 3、调用loadServiceMethod()方法解析数据,如果缓存中有,直接拿
  • 4、用serviceMethod和args作为参数,构造一个OkHttpCall对象
  • 5、调用serviceMethod的callAdapter对象的adapt(OkHttpCall)方法获取一个代理实现类的对象。

PS:

  • 1、这里说下OkHttpCall是实现retrofit2.Call接口的子类
  • 2、serviceMethod.callAdapter是DefaultCallAdapterFactory里面new的匿名内部类,这个匿名内部类的adapt方法直接返回就是retrofit2.Call,因为OkHttpCall是retrofit2.Call的子类,所以返回的是OkHttpCall这个实例本身。

简易的流程图如下:


image.png

(三)、执行请求

执行请求主要分为两个部分,一个是执行同步请求,一个是执行异步请求
我们的demo是同步请求,我们先讲解同步请求,然后再讲解异步请求

1、同步请求详解

同步代码如下:

List<Contributor> contributors = call.execute().body();

因为OkHttpCall是实现retrofit2.Call接口的具体实现类,所以
call.execute()实际调用是OkHttpCall的execute()方法

1.1、OkHttpCall的execute()方法详解

 @Override public Response<T> execute() throws IOException {
    okhttp3.Call call;
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;
      if (creationFailure != null) {
        if (creationFailure instanceof IOException) {
          throw (IOException) creationFailure;
        } else {
          throw (RuntimeException) creationFailure;
        }
      }
      call = rawCall;
      if (call == null) {
        try {
          call = rawCall = createRawCall();
        } catch (IOException | RuntimeException e) {
          creationFailure = e;
          throw e;
        }
      }
    }
    if (canceled) {
      call.cancel();
    }
    return parseResponse(call.execute());
  }

1.2、OkHttpCall的createRawCall()方法详解

然后是调用的createRawCall()l来获取一个okhttp3.Call,然后调用
parseResponse()来获取retrofit2.Response。

  private okhttp3.Call createRawCall() throws IOException {
    Request request = serviceMethod.toRequest(args);
    okhttp3.Call call = serviceMethod.callFactory.newCall(request);
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }
  • 1、调用serviceMethod的toRequest(args)来得到okhttp3.Request对象
  • 2、调用serviceMethod的callFactory对象的newCall(request)来获取一个okHttpCall。

让我们看下ServiceMethod类的toRequest()方法

1.3、ServiceMethod类的toRequest()方法详解

  Request toRequest(Object... args) throws IOException {
    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
        contentType, hasBody, isFormEncoded, isMultipart);
    @SuppressWarnings("unchecked") 
    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
    int argumentCount = args != null ? args.length : 0;
    if (argumentCount != handlers.length) {
      throw new IllegalArgumentException("Argument count (" + argumentCount
          + ") doesn't match expected count (" + handlers.length + ")");
    }
    for (int p = 0; p < argumentCount; p++) {
      handlers[p].apply(requestBuilder, args[p]);
    }
    return requestBuilder.build();
  }

通过上面代码我们知道

  • 1、构建了一个RequestBuilder的对象
  • 2、获取ServiceMethod的parameterHandlers
  • 3、为了保证参数注解和参数的数目的一致性,做了一致性检查
  • 4、遍历parameterHandlers,根据不同的参数注解,配置RequestBuilder的相应属性
  • 5、最后调用RequestBuilder的build()方法来获取一个okhttp3.Request对象

那我们来看下RequestBuilder的build()方法

1.4、ServiceMethod类的RequestBuilder的build()方法详解

 Request build() {
    HttpUrl url;
    HttpUrl.Builder urlBuilder = this.urlBuilder;
    if (urlBuilder != null) {
      url = urlBuilder.build();
    } else {
      url = baseUrl.resolve(relativeUrl);
      if (url == null) {
        throw new IllegalArgumentException(
            "Malformed URL. Base: " + baseUrl + ", Relative: " + relativeUrl);
      }
    }
    RequestBody body = this.body;
    if (body == null) {
      // Try to pull from one of the builders.
      if (formBuilder != null) {
        body = formBuilder.build();
      } else if (multipartBuilder != null) {
        body = multipartBuilder.build();
      } else if (hasBody) {
        body = RequestBody.create(null, new byte[0]);
      }
    }
    MediaType contentType = this.contentType;
    if (contentType != null) {
      if (body != null) {
        body = new ContentTypeOverridingRequestBody(body, contentType);
      } else {
        requestBuilder.addHeader("Content-Type", contentType.toString());
      }
    }
    return requestBuilder
        .url(url)
        .method(method, body)
        .build();
  }

通过上面代码我们知道build()方法流程如下:

  • 1、根据HttpUrl.Builder来获取HttpUrl
  • 2、根据不同的类型来构造RequestBody
  • 3、设置MediaType
  • 4、调用RequestBuilder.url(url)和method(method, body)方法来设置RequestBuilder的HttpUrl和请求方式和请求体。
  • 5、调用build来来获取一个okhttp3.Request对象(build()方法内部是直接new的一个Request)

注意事项:

  • retrofit2.RequestBuilder类中有一个字段private final okhttp3.Request.Builder requestBuilder; 大家千万不要弄混了。
    -retrofit2.RequestBuilder的构造函数完成一些初始化,由于简书篇幅的限制,我这里就没说,大家最好也去看下。

这时候我们再回来看下OkHttpCall中的createRawCall()内部

Request request = serviceMethod.toRequest(args);

这个流程我们已经讲解完了。

okhttp3.Call call = serviceMethod.callFactory.newCall(request);

其实就是调用serviceMethod的callFactory对象的newCall(request)方法来获取一个okhttp3.Call对象,这个流程是在okhttp里面,我就不详细说了。

这时候我们又回到了OkHttpCall的execute()的里面,最后调用了parseResponse(call.execute()),截止到call.execute()我们一直都是在解析,构造请求,但是到call.execute(),我们正式发起HTTP请求了。parseResponse方法主要是解析响应体。我们一会再讲解。

2、异步请求详解

call.enqueue(new Callback<List<Contributor>>() {
    @Override
    public void onResponse(Call<List<Contributor>> call, Response<List<Contributor>> response) {
        List<Contributor> contributorList = response.body();
        for (Contributor contributor : contributorList){
            Log.d("login", contributor.getLogin());
            Log.d("contributions", contributor.getContributions() + "");
        }
    }
    @Override
    public void onFailure(Call<List<Contributor>> call, Throwable t) {
    }
});

因为OkHttpCall是实现retrofit2.Call接口的具体实现类,所以
call.enqueue()实际调用是OkHttpCall的enqueue()方法。那我们来看下OkHttpCall的enqueue()方法的源码

@Override public void enqueue(final Callback<T> callback) {
    if (callback == null) throw new NullPointerException("callback == null");
    okhttp3.Call call;
    Throwable failure;
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;
      call = rawCall;
      failure = creationFailure;
      if (call == null && failure == null) {
        try {
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          failure = creationFailure = t;
        }
      }
    }
    if (failure != null) {
      callback.onFailure(this, failure);
      return;
    }
    if (canceled) {
      call.cancel();
    }
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
          throws IOException {
        Response<T> response;
        try {
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          callFailure(e);
          return;
        }
        callSuccess(response);
      }
      @Override public void onFailure(okhttp3.Call call, IOException e) {
        try {
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
      private void callFailure(Throwable e) {
        try {
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }

      private void callSuccess(Response<T> response) {
        try {
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
    });
  }

其实内部也一样通过调用createRawCall()(createRawCall()方法内部的实现逻辑,我这里就不说了)方法来获取okhttp3.Call对象,然后调用okhttp3.Call的enqueue()方法来实现的。如果请求成功则调用parseResponse来解析响应体,解析过程中没有问题则调用callSuccess()方法,如果解析出现问题则调用callFailure()方法,其实callFailure()内调用的是callback.onFailure()方法;如果在请求过程中出现问题则也一样调用callback.onFailure()。

四、处理响应

1、Response<T> parseResponse(okhttp3.Response) 方法详解

处理响应主要是调用Response<T> parseResponse(okhttp3.Response) 方法来解析响应体

 Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();
    rawResponse = rawResponse.newBuilder()
        .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
        .build();
    int code = rawResponse.code();
    if (code < 200 || code >= 300) {
      try {
        ResponseBody bufferedBody = Utils.buffer(rawBody);
        return Response.error(bufferedBody, rawResponse);
      } finally {
        rawBody.close();
      }
    }
    if (code == 204 || code == 205) {
      rawBody.close();
      return Response.success(null, rawResponse);
    }
    ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
    try {
      T body = serviceMethod.toResponse(catchingBody);
      return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
      catchingBody.throwIfCaught();
      throw e;
    }
  }

通过研究parseResponse代码,我们知道:

  • 首先,Retrofit是先取出ResponseBody。
  • 然后,调用rawResponse.newBuilder()的build()方法构建一个*** 空的 ***响应的体的rawResponse。
  • 其次,根据HTTP状体码来判断是否成功与失败
  • 再次,如果成功且是204或者205则说明内容或者重复内容则不用关心响应体,则调用Response.success(null, rawResponse)方法
  • 最后,调用ServiceMethod实例的toResponse(catchingBody)方法来获取一个类型是T的对象

2、ServiceMethod类的parseResponse() 方法详解

  R toResponse(ResponseBody body) throws IOException {
    return responseConverter.convert(body);
  }

可见ServiceMethod类的parseResponse() 方法内部调用的是responseConverter.convert(body),responseConverter我们之前说过是ServiceMethod.Builder的build()方法中获取的通过调用createResponseConverter()来获取的,在上文我们知道这里的responseConverter是GsonResponseBodyConverter。那我们来看下GsonResponseBodyConverter的convert的内部实现

  @Override public T convert(ResponseBody value) throws IOException {
    JsonReader jsonReader = gson.newJsonReader(value.charStream());
    try {
      return adapter.read(jsonReader);
    } finally {
      value.close();
    }
  }

这样就实现了网络响应体的反序列化。
自此我们的ServiceMethod的toRespons()方法我们就解析完毕。

3、Response.success(body, rawResponse)方法详解

  public static <T> Response<T> success(T body, okhttp3.Response rawResponse) {
    if (rawResponse == null) throw new NullPointerException("rawResponse == null");
    if (!rawResponse.isSuccessful()) {
      throw new IllegalArgumentException("rawResponse must be successful response");
    }
    return new Response<>(rawResponse, body, null);
  }

这里面很简单,就是作为参数new了一个retrofit2.Response对象,然我们来看下retrofit2.Response的构造函数

  private Response(okhttp3.Response rawResponse, T body, ResponseBody errorBody) {
    this.rawResponse = rawResponse;
    this.body = body;
    this.errorBody = errorBody;
  }

至此,Response.success()方法我们分析关闭,至于Response. error()方法分析,请大家看我之前的文章。

所以同步或者异步请求的响应 我们就可以通过 response.body()来获取对应的对象。

同步流程图如下:


同步.png

三、总结

我们再回过头来再来分析一下这张图
其实整个Retrofit的流程如下图:


retrofit.png

四、okHttp+Retrofit的整体架构

为了让大家更好的理解层级的结构,简单做了下面这张图,在层级上让大家更好的理解层级的结构


层级.png

如果加上大家自定义的interface和里面的方法,大体的结构如下:


retrofit结构.png

如果把上面的这张图立起来,就是下面这张图:


retrofit结构.png

看完这三张图,希望大家对Retrofit的理解能更深一步!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容