Retrofit-原理全解析

1.Retrofit介绍

Retrofit是一个RESTful的Http网络请求框架的封装,网络请求部分本质是由OKHttp完成的

而我们学习Retrofit除了对Http请求有进一步的了解之外,我们还能通过学习Retrofit 源码感受到面向对象特性运用的极致与合理运用设计模式带来的代码优雅。

本篇内容我是面向已经会用Retrofit的开发者写的,不会再重复介绍Retrofit用法,如果还不会使用Retrofit我建议先去看看官方文档后再来学习本文章,那么事不宜迟,我们开始吧!

2.原理流程

本文将会从源码角度,讲述Retrofit从初始化,到最后请求回调到开发者这段过程究竟发生了什么进行详细分析解说。

2.0 创建Retrofit对象

我们通过了解Retrofit初始化的过程,可以管中窥豹的了解Retrofit所需什么东西,能对Retrofit有一个初步的了解。

2.0.1 通过建造者模式创建Retrofit对象

因为Retrofit初始化时可选参数特别的多,所以Retrofit用到建造者模式来创建对象,我们可以看看源码中Retrofit是怎么实现建造者模式的。

public final class Retrofit {
  
    //...some members
    
    //1.Retrofit的构造器是外部不可见状态,需要通过Builder创建
    Retrofit(
      okhttp3.Call.Factory callFactory,
      HttpUrl baseUrl,
      List<Converter.Factory> converterFactories,
      List<CallAdapter.Factory> callAdapterFactories,
      @Nullable Executor callbackExecutor,
      boolean validateEagerly) {
      
    //init something...
    }
 
    //...some functions
    
    //2.Retrifit的Builder
    public static final class Builder {
      
        //...some members
    
        Builder(Platform platform) {
          this.platform = platform;
        }
        
        public Builder() {
          //3.Retrofit自己寻找合适的平台通过,目前有Android与Java,通过获取虚拟机的名称判断是否Android和Java
          this(Platform.get());
        }
    }
    
    //set something...
    
    //4.通过build函数创建Retrofit对象,下面章节再build详细介绍方法
     public Retrofit build() {
      // do something...
      return new Retrofit(...)
    }
    

通过阅读上面源码,我们得知以下两点

  1. Retrofit对外隐藏了构造器
  2. 提供了Builder对象,调用build()创建Retrofit对象

那么,Builder提供了什么set函数给我们初始化Retrofit呢?我们展开看看

2.0.2 Retrofit.Builder详细介绍

我们直接通过阅读源码来介绍Retrofit.Builder的详情吧~

  public static final class Builder {
        //介绍常用初始化函数
        //1.设置用于请求Http的客户端,一般为OkHttpClient
        public Builder client(OkHttpClient client) {
          return callFactory(Objects.requireNonNull(client, "client == null"));
        }   
        //1.1 设置生产OkHttpCall(真正做请求)的工厂
         public Builder callFactory(okhttp3.Call.Factory factory) {
          this.callFactory = Objects.requireNonNull(factory, "factory == null");
          return this;
       }

        
        /**
        2.设置全局基础域名
        这样我们声明接口的时候,就只需要填入路径即可。
        Retrofit还提供以下重载方法:
        public Builder baseUrl(URL baseUrl)
        public Builder baseUrl(HttpUrl baseUrl) {
        */
        public Builder baseUrl(String baseUrl) {
          Objects.requireNonNull(baseUrl, "baseUrl == null");
          return baseUrl(HttpUrl.get(baseUrl));
        }
        
        /**
        3.设置数据(请求、响应)转换器工厂
            1.某些请求(Body、PartMap等),需要用到转换器
            2.在接收响应时,也会用到转换器
        在多场景用到不同转换器的时候,在设计模式中肯定使用工厂模式来生产对应的转换器,统一了生产逻辑
        */
        public Builder addConverterFactory(Converter.Factory factory) {
            converterFactories.add(Objects.requireNonNull(factory, "factory == null"));
            return this;
        }
        
        /**
        4.设置请求适配器工厂,用于生产实际请求时做请求逻辑的对象
        我个人对这个类的理解就是Retrofit给开发者提供了hook请求时候的机会,我们可以稍微通过DefaultCallAdapterFactory得知其会默认返回ExecutorCallbackCall,对真正做请求动作的OkHttpCall进行代理
        所以我们可以通过自定义这个CallFactor生产我们自己自定义过的CallAdapter,在整个流程中植入我们的逻辑,比如做一些缓存工作之类的,或者公共的逻辑(比如判断接口状态,做出相应行为)
        */
        public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
          callAdapterFactories.add(Objects.requireNonNull(factory, "factory == null"));
          return this;
        }
    
        /**
        5.设置线程调度器,主要用于ExecutorCallbackCall中,请求接口后从子线程切换到主线程用
        */
        public Builder callbackExecutor(Executor executor) {
          this.callbackExecutor = Objects.requireNonNull(executor, "executor == null");
          return this;
       }
    }

2.0.3 build函数

阅读Build函数是为了了解当我们没有为某些参数赋值时,Retrofit会为这些参数设置怎么样的缺省值,我们可以通过了解缺省值来了解Retrofit会怎么去实现某一块的实现。

public Retrofit build() {
    //1.设置全局基础域名
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }
    
    //2.callFactory判空,默认为OkHttpClient
      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }
    //3.线程调度器判空,默认为平台默认实现的调度器,Android的调度器默认用Handle(MainLooper)实现
      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

    /**
        4.除了添加自定义的请求适配器,在最后还会添加平台默认请求适配器作为保底,Android的默认实现配合了
        线程调度器,在异步调用时,使用线程调度器切换回主线程
    */
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

    /**
        5.除了添加自定义的转换器,会首先插入Retrofit对某些请求类型预置的转换器(如Streaming)
    */
      List<Converter.Factory> converterFactories =
          new ArrayList<>(
              1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
      //添加预置转换器
      converterFactories.add(new BuiltInConverters());
      //添加自定义转换器
      converterFactories.addAll(this.converterFactories);
      //添加平台默认转换器(Android没有默认转换器)
      converterFactories.addAll(platform.defaultConverterFactories());
      //
      return new Retrofit(
          callFactory,
          baseUrl,
          unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories),
          callbackExecutor,
          validateEagerly);
    }

以上就是Retrofit的初始化过程,了解这个过程我们得知了Retrofit初始化所需的参数~以及他们的作用。

接下来就开始进入核心流程,Retrofit是怎么通过接口创建出对象,又是怎么通过这个对象,进行网络请求的。

2.1 Retrofit通过create(T)创建对象做了什么?

public <T> T create(final Class<T> service) {
    /**
        1.检查接口是否合法
    */
    validateServiceInterface(service);
    /**
        2.通过动态代理创建对象(详情可返回上面看动态代理的介绍)
    */
    return (T)
        Proxy.newProxyInstance(
            service.getClassLoader(),
            new Class<?>[] {service},
            new InvocationHandler() {   // 3.通过代理对象调用的函数最后都会调用该回调对象中的invoke函数
              private final Platform platform = Platform.get();
              private final Object[] emptyArgs = new Object[0];

              @Override
              public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
                  throws Throwable {
                //4.如果该对象是Object,则直接通过反射调用Object自身函数
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args);
                }
                args = args != null ? args : emptyArgs;
                return platform.isDefaultMethod(method)//5.判断是否Java8中的默认方法,如果是的话则调用默认方法
                    ? platform.invokeDefaultMethod(method, service, proxy, args)
                    : loadServiceMethod(method).invoke(args);//7.这个实际上调用了两个函数,我们在2.2章节中详细剖析
              }
            });
  }

总结
我们通过Retrofit的create方法,可以将一个接口生成为一个对象,其中原理就是通过Java的动态代理创建一个代理对象,其中任何的函数调用,都会通过InvocationHandler回调对象中的invoke函数回调,最后经过该对象的合法性检查后,会分别调用loadServiceMethodloadServiceMethod返回对象的invoke方法。
接下来我们就把Retrofit的面纱揭个不剩吧!

2.2 通过动态代理创建的对象调用方法会发生什么事?

上一章解释了Retrofit是怎么创建对象的,但是留下了两个问题,当我们调用Retrofit生成相应对象的函数后,这个网络请求是怎么传出去的?

loadServiceMethod(method).invoke(args)

我们接下来就聚焦这2个函数,看看Retrofit到底做什么?

2.2.1 loadServiceMethod

我们可以通过函数名与参数推断出这个函数的用途,就是加载这个接口方法,我们来看看loadServiceMethod源码

 ServiceMethod<?> loadServiceMethod(Method method) {
    //1.从缓存中获取一个ServiceMethod对象
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      //2.如果缓存中没有,则解析该方法,然后放进缓存
      if (result == null) {
        result = ServiceMethod.parseAnnotations(this, method);
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

我们可以看出该函数只做两件事:

  1. 从缓存中获取serviceMethod
  2. 如果缓存中没有,则通过ServiceMethod.parseAnnotations解析方法,并且放入缓存中。

接下来我们看一下比较核心的,Retrofit是怎么将一个接口中的函数,解析成一个ServiceMethod对象。
但是在这之前我们必须先搞清楚ServiceMethod对象是什么?
我们可以先简单的给ServiceMethod一个定义,它主要负责保存请求中相关的信息,我们可以通过它的子类构造函数推断出来。

//ServiceMethod的子类
class HttpServiceMethod{

     HttpServiceMethod(
          RequestFactory requestFactory,//保存请求全部信息的工厂(负责保存头部信息)
          okhttp3.Call.Factory callFactory,//OKHttp请求对象工厂(负责请求)
          Converter<ResponseBody, ResponseT> responseConverter)(数据转换器)
          {
            this.requestFactory = requestFactory;
            this.callFactory = callFactory;
            this.responseConverter = responseConverter;
         }
         
}

那么我们继续往后走,看看Retrofit怎么将函数解析为ServiceMethod的。

2.2.1.1 封装与解析请求

刚刚我们跟到ServiceMethod.parseAnnotations这个方法,那么我们看看其源码做了什么?

abstract class ServiceMethod<T> {
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    //1.真正解析请求参数的地方,最后会将解析后的参数保存到RequestFactory中(在生成OkHttp的Request对象时,就是通过RequestFactory来生产)
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
    //2.判断返回值是否合法,不合法时抛出错误
    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.");
    }
    //3.刚刚只是解析了请求相关的东西,这个方法接着会完整的封装整个请求为一个ServiceMethod
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }

  abstract @Nullable T invoke(Object[] args);
}

总结一下,这个静态方法一共做了三件事:

  1. 解析请求参数,最后封装成一个RequestFactory
  2. 检测返回值类型是否合法
  3. 封装一个完整的ServiceMethod

接着我们需要确认两件事:

  1. 怎么通过反射解析Method后封装成RequestFactory
  2. ServiceMethod是长什么样子的?
2.2.1.1.1 确认请求类型(解析Method上的注解)

首先要解开上面的第一个疑问,怎么通过反射解析Method后封装成RequestFactory?我们看看 RequestFactory.parseAnnotations(retrofit, method)源码

final class RequestFactory {
 //1.静态方法,通过Builder来构造Factory
 static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
    return new Builder(retrofit, method).build();
  }
  ...
}

接着我们深入的RequestFactory的Builder看看是怎么build的,因为源码太过庞大,我们只截关键部分代码

static final class Builder {

    ... some members
    
    //解析请求类型后会将结果保存到该字段    
    @Nullable String httpMethod;
    //方法上的注解
    final Annotation[] methodAnnotations;
    //参数上的注解
    final Annotation[][] parameterAnnotationsArray;
    //方法参数
    final Type[] parameterTypes;
    
    //0.构造函数,主要将method中的注解,参数拿来出来方便等等解析
    Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      this.methodAnnotations = method.getAnnotations();
      this.parameterTypes = method.getGenericParameterTypes();
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }

    RequestFactory build() {
    //1.主要确认请求类型(POST、GET...),最后会将结果保存到httpMethod字段,还有其他如请求头之类的注解
      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }
    //2.进行合法判断,这里不展开说
      ...checkMethods
      
      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
    //3.根据请求类型解析将每个参数解析成ParameterHandler对象,
      for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
        parameterHandlers[p] =
            parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
      }
    //4.将解析好的Builder对象通过RequestFactory构造函数传入
      return new RequestFactory(this);
    }
}

我们再单独看看RequestFactory的构造函数是怎么样的

  RequestFactory(Builder builder) {
    method = builder.method;//java反射中的Method对象
    baseUrl = builder.retrofit.baseUrl;//全局基础域名
    httpMethod = builder.httpMethod;//请求类型
    relativeUrl = builder.relativeUrl;//相对路径
    headers = builder.headers;//请求头参数
    contentType = builder.contentType;
    hasBody = builder.hasBody;
    isFormEncoded = builder.isFormEncoded;
    isMultipart = builder.isMultipart;
    parameterHandlers = builder.parameterHandlers;//解析好的参数(ParameterHandler类型)
    isKotlinSuspendFunction = builder.isKotlinSuspendFunction;//是否Kotlin的挂起函数
  }

可以看出Builder发挥了它的作用,将所需参数解包至Factory,而具体参数如上图注释所见。

说到这里,我们还不知道Retrofit是怎么解析方法上的请求方式与参数,接下来就深入说明这两方面

2.2.1.1.2 确认请求类型
//RequestFactory内部类Builder
static final class Builder {

//1.通过判断注解类型来确认方法是什么请求
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);
      } 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());
      } 
      //...省略一些Header和Part的解析函数
    }
    
    //2.通过该方法进行真正的解析
    private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
    //3.判断之前会否解析过,解析过则抛出异常
      if (this.httpMethod != null) {
        throw methodError(
            method,
            "Only one HTTP method is allowed. Found: %s and %s.",
            this.httpMethod,
            httpMethod);
      }
      //4.一般为字符串如"POST","GET"之类
      this.httpMethod = httpMethod;
      //5.是否包含请求正文
      this.hasBody = hasBody;

      if (value.isEmpty()) {
        return;
      }

      //6.判断注解中的地址合法性
      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;
      //7.收集路径上的参数并出重
      this.relativeUrlParamNames = parsePathParameters(value);
    }

通过以上源码我们可以得知,我们在接口函数上声明的注解请求类型在代码中也是通过简单的对比类型确认的,再就是一些合法性的判断,保证跟声明规范一致就可以了

接下来我们看看参数是怎么样解析的。

2.2.1.1.3 确认请求参数

因参数类型太多,我在下面的源码中只保留了高频的类型~

private @Nullable ParameterHandler<?> parseParameter(
        int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) {
      ParameterHandler<?> result = null;
      
      if (annotations != null) {
        for (Annotation annotation : annotations) {
        //1.循环遍历Method中的参数,解析注解与参数类型
          ParameterHandler<?> annotationAction =
              parseParameterAnnotation(p, parameterType, annotations, annotation);

          if (annotationAction == null) {
            continue;
          }

          if (result != null) {
            throw parameterError(
                method, p, "Multiple Retrofit annotations found, only one allowed.");
          }

          result = annotationAction;
        }
      }
    //2.判断是否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;
    }
    
     @Nullable
    private ParameterHandler<?> parseParameterAnnotation(
        int p, Type type, Annotation[] annotations, Annotation annotation) {
        //3.判断参数注解类型,根据注解类型返回对应的ParameterHandler
        //4.解析Query类型参数
       if (annotation instanceof Query) {
       //4.1 检查合法性
        validateResolvableType(p, type);
        Query query = (Query) annotation;
        //4.2 获取参数名字
        String name = query.value();
        //4.3 是否需要编码
        boolean encoded = query.encoded();
        //4.3 获取参数类型
        Class<?> rawParameterType = Utils.getRawType(type);
        //4.4 标记已经含有Query参数(用于其他类型的参数解释时判断合法性问题)
        gotQuery = true;
        //4.5 判断是否是实现了遍历器接口
        if (Iterable.class.isAssignableFrom(rawParameterType)) {
          //...省略部分安全检测
          ParameterizedType parameterizedType = (ParameterizedType) type;
          Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
          Converter<?, String> converter = retrofit.stringConverter(iterableType, annotations);
          //4.5.1 返回解析遍历器类型属性专用的ParameterHandler
          return new ParameterHandler.Query<>(name, converter, encoded).iterable();
        } else if (rawParameterType.isArray()) { //4.6 判断是否是数组
          Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
          Converter<?, String> converter =
              retrofit.stringConverter(arrayComponentType, annotations);
           //4.5.2 返回解析数组类型属性专用的ParameterHandler
          return new ParameterHandler.Query<>(name, converter, encoded).array();
        } else {
        //4.7 不是数组等集合类型则直接返回ParameterHandler.Query
          Converter<?, String> converter = retrofit.stringConverter(type, annotations);
          return new ParameterHandler.Query<>(name, converter, encoded);
        }
      } else if (annotation instanceof Body) { //5.如果参数类型是Body的话
        //...省略部分安全检测
        Converter<?, RequestBody> converter;
        try {
        //5.1 从Retrofit中获取转换器,可以看出Body是通过转换器解析
          converter = retrofit.requestBodyConverter(type, annotations, methodAnnotations);
        } catch (RuntimeException e) {
          // Wide exception range because factories are user code.
          throw parameterError(method, e, p, "Unable to create @Body converter for %s", type);
        }
        gotBody = true;
        //5.2 将转换器塞进去ParameterHandler里
        return new ParameterHandler.Body<>(method, p, converter);

      }  
      return null; // Not a Retrofit annotation.
    }

通过上面的解析参数的源码我们可以得知,根据参数的请求类型返回一个对应的ParameterHandler对象,而ParameterHandler作用在后面请求的时候会说到。
在这里我们就先简单的介绍ParameterHandler,它的定义就是参数处理器器,作用是当RequestFactory开始生产OkHttp的Request对象时(OkHttp中负责发送请求的对象),会遍历解析出来的ParameterHandler对象,再将负责构建Request的RequestBuilder对象委托给ParameterHandler处理,每个类型的ParameterHandler就会根据自己实际的数据封装逻辑,将请求参数以自己的规则与RequestBuilder进行交互,最后再通过RequestBuilder生产出Request对象,最后发出请求。

结合源码看看ParameterHandler的作用:

  okhttp3.Request create(Object[] args) throws IOException {
    //1.将刚刚解析的参数ParameterHandler取出
    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
    //2.判断参数数量是否一致
    int argumentCount = args.length;
    if (argumentCount != handlers.length) {
      throw new IllegalArgumentException(
          "Argument count ("
              + argumentCount
              + ") doesn't match expected count ("
              + handlers.length
              + ")");
    }
    //3.构建RequestBuilder,准备最后生成发送请求的Request对象
    RequestBuilder requestBuilder =
        new RequestBuilder(
            httpMethod,
            baseUrl,
            relativeUrl,
            headers,
            contentType,
            hasBody,
            isFormEncoded,
            isMultipart);
    //4.如果是挂起函数,则特殊处理一下
    if (isKotlinSuspendFunction) {
      // The Continuation is the last parameter and the handlers array contains null at that index.
      argumentCount--;
    }
    //5.遍历参数处理器,调用apply方法,参数处理器会根据自身的规则将请求参数设置进RequestBuilder中
    List<Object> argumentList = new ArrayList<>(argumentCount);
    for (int p = 0; p < argumentCount; p++) {
      argumentList.add(args[p]);
      handlers[p].apply(requestBuilder, args[p]);
    }
    //6.生成Request对象(不是重点就不展开说了)
    return requestBuilder.get().tag(Invocation.class, new Invocation(method, argumentList)).build();
  }


最后举例Query的ParameterHandler如何将请求数据放进RequestBuilder中:

static final class Query<T> extends ParameterHandler<T> {
    private final String name;
    private final Converter<T, String> valueConverter;
    private final boolean encoded;

    Query(String name, Converter<T, String> valueConverter, boolean encoded) {
      this.name = Objects.requireNonNull(name, "name == null");
      this.valueConverter = valueConverter;
      this.encoded = encoded;
    }

    @Override
    void apply(RequestBuilder builder, @Nullable T value) throws IOException {
      if (value == null) return; // Skip null values.

      String queryValue = valueConverter.convert(value);
      if (queryValue == null) return; // Skip converted but null values
    //1.通过RequestBuilder的addQueryParam添加数据
      builder.addQueryParam(name, queryValue, encoded);
    }
  }

最后结论是,每种请求参数类型都会自己一套添加到RequestBuilder的具体规则,每种请求参数类型通过继承ParameterHandler来实现这种具体规则,算是将封装和多态玩爆了。

最后再回顾一下RequestFactory的构造函数,解析请求这里算是说完了。

final class RequestFactory {
  RequestFactory(Builder builder) {
    method = builder.method;//java反射中的Method对象
    baseUrl = builder.retrofit.baseUrl;//全局基础域名
    httpMethod = builder.httpMethod;//请求类型
    relativeUrl = builder.relativeUrl;//相对路径
    headers = builder.headers;//请求头参数
    contentType = builder.contentType;
    hasBody = builder.hasBody;
    isFormEncoded = builder.isFormEncoded;
    isMultipart = builder.isMultipart;
    parameterHandlers = builder.parameterHandlers;//解析好的参数(ParameterHandler类型)
    isKotlinSuspendFunction = builder.isKotlinSuspendFunction;//是否Kotlin的挂起函数
  }
}

2.2.1.2 生成请求适配器(用于确认发起请求与接口回调)

因为篇幅过长,可能已经丢失了前面的路径了,我画一个简单的流程图帮助大家回忆一下目前我们到哪一步了

Retrofit-原理全解析_第1张图片

从流程图得知,我们已经了解了怎么创建对象,也知道了调用创建对象之后,我们需要获取ServiceMethod对象,也知道了ServiceMethod的请求参数是怎么来的了,接着肯定是如何发出请求了~不要着急,我们继续看源码回忆一下我们学到哪里了?

abstract class ServiceMethod<T> {
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    //1.如何解析请求参数->刚讲完这里
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

    //...省略部分代码
    //2.返回一个完整的ServiceMethod->准备讲到这里
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }

  abstract @Nullable T invoke(Object[] args);
}
2.2.1.2.1 封装ServiceMethod对象

在这里需要再重申一次ServiceMethod的对象的作用:

  1. 保存一个请求所需的一切
    1. 请求路径、请求类型、请求参数(已了解)
    2. 解析返回值的方法(数据转换器)(未了解)
    3. 请求回调监听(未了解)
  2. 拥有发起请求与接收结果的能力(未了解)

刚刚我们已经了解了如何解析请求,接下来就了解上面标记未了解的地方,如何生成ServiceMethod:

abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {
    static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
          Retrofit retrofit, Method method, RequestFactory requestFactory) {
        //1.判断是否挂起方法
        boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
        Annotation[] annotations = method.getAnnotations();
        Type adapterType;
        if (isKotlinSuspendFunction) {
            //1.1 ...如果是挂起方法,则做相关逻辑 日后会另外开章节说明
        } else {
            //1.2通过传入的method获取返回类型
          adapterType = method.getGenericReturnType();
        }
        /**
            2.这个函数是通过我们一开始为Retrofit设置的CallAdapterFactory中获取CallAdapter,关于CallAdapter的作用重新回顾一下,就是Retrofit为开发者提供Hook请求过程的一个代理对象,我们可以通过自己的CallAdapter决定怎么发送请求,怎么接收响应
        */
        CallAdapter<ResponseT, ReturnT> callAdapter =
            createCallAdapter(retrofit, method, adapterType, annotations);
        Type responseType = callAdapter.responseType();
        //...省略一些安全校验
        /**
            3.同样的,这里从我们的转换器工厂中获得转换器,用于响应时候的数据转换
        */
        Converter<ResponseBody, ResponseT> responseConverter =
            createResponseConverter(retrofit, method, responseType);
        /**
            4.callFactory在介绍Retrofit初始化也介绍过,callFactory生产真正用于OkHttp的请求对象
        */
        okhttp3.Call.Factory callFactory = retrofit.callFactory;
        //5.创建CallAdapter,也就是ServiceMethod的实现子类,我们看看其构造函数
        if (!isKotlinSuspendFunction) {
          return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
        } else if (isKotlinSuspendFunction) {
          //省略挂起函数相关,后面会另开章节说明
        }  
      }
  }
  
  //6.ServiceMethod的子类
  static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {
    private final CallAdapter<ResponseT, ReturnT> callAdapter;
    
    //7.构造函数
    CallAdapted(
        RequestFactory requestFactory,
        okhttp3.Call.Factory callFactory,
        Converter<ResponseBody, ResponseT> responseConverter,
        CallAdapter<ResponseT, ReturnT> callAdapter) {
      //8.我们可以从构造函数中看出,CallAdapted保存了请求数据、请求器工厂、数据转换器
      super(requestFactory, callFactory, responseConverter);
      //9.保存了我们的callAdapter(也就是callAdapterFactory生产的代理请求对象)
      this.callAdapter = callAdapter;
    }
    //10.暂时保留这一段逻辑~后面会说到
    @Override
    protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
      return callAdapter.adapt(call);
    }
  }

讲到这里,我们已经了解了在接口中声明的函数是怎么一步一步解析为一个完整的ServiceMethod

  1. 通过反射,解析中的请求类型注解和参数注解与参数类型,并且封装到RequestFactory中,最后RequestFactory保存如下数据:
    1. 请求类型(Post、GET…)
    2. 请求头(这里没展开说)
    3. ContentType
    4. ParameterHandler(参数处理器)
    5. relativeUrl(请求路径)
  2. 封装一个完整ServiceMethod
    1. 保存了返回数据类型
    2. 保存了CallAdapter(一个给我们提供干预机会的请求适配器)
    3. 保存了返回数据转换器
    4. 保存了请求器生产工厂
    5. 保存了请求数据生产工厂
  3. 缓存ServiceMethod

可以说2.2.1整个章节介绍了Retrofit如何为一个请求做准备~现在该有的东西都准备好了,最后会将这个解析好的ServiceMethod放进缓存,这样重复调用时,就不用再解析一轮了。

接下来,我们就要上面保存好的东西,到底怎么运行起来,我们了解完准备阶段后,接着就是真正的请求行为阶段

loadServiceMethod(method).invoke(args);

基本解释完loadServiceMethod,接下来就是解析invoke了~

2.2.2 通过ServiceMethod的invoke函数发起网络请求

  @Override
  final @Nullable ReturnT invoke(Object[] args) {
    //稍等在真正发送请求的时候再正式介绍OkHttpCall
    Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    //1.调用了adapt函数
    return adapt(call, args);
  }

我直接截取invoko的具体实现函数,这个是ServiceMethod的子类HttpServiceMethod,我们可以看出当调用invoke时,仅仅创建出OkHttpCall对象,并且将解析阶段的数据保存到OkHtppCall中,就调用了adapt函数,将call与参数数组作为参数传递过去,我们先不探究Call对象,我们先跟下去,看看adapt做了什么?

//1.随着源码跟回了CallAdapted的逻辑中
 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;
    }

    @Override
    protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
    /**
    2.调用了callAdapter中的adapt函数,将刚刚构建的OkHttpCall对象扔了进去,然后生产了一个负责发送请求的对象给开发者,我们继续深入看看callAdapter.adapt(call)
    */
      return callAdapter.adapt(call);
    }
  }
  
  /**
  3.只截取了默认CallAdapterFactory具体生产的callAdapter相关逻辑
  */
  new CallAdapter<Object, Call<?>>() {
                public Type responseType() {
                    return responseType;
                }
                //4.上面第二步中实际调用的逻辑,我们可以看到,返回了一个DefaultCallAdapterFactory.ExecutorCallbackCall,传入了OkHttpCall与executor,这里的executor是用于切换线程用
                public Call<Object> adapt(Call<Object> call) {
                    return (Call)(executor == null ? call : new DefaultCallAdapterFactory.ExecutorCallbackCall(executor, call));
                }
            };

看到这里,虽然弯弯绕绕,但是确认了在默认实现中最后返回的是DefaultCallAdapterFactory.ExecutorCallbackCall对象,我们在进行异步或者同步请求时,都是通过这个对象发起请求的,那么接下来,我们终于终于到了最重要的部分,我们的Rertrofit请求器封装好了,参数也解析好了,各种配置都准备好了,最后就是只欠调用!

2.2.2.1 开始请求发生了什么?

我们一般发送请求是这么写的:

fun request(){
    //1.从retrofit中获取请求对应的接口实例(经过动态代理)
    val apiInterfaceImpl = retrofit.create(ApiInterface::class.java)
    //2.通过代理对象对应的函数获得请求对象(默认是DefaultCallAdapterFactory.ExecutorCallbackCall)
    val call = apiInterfaceImpl.requestList()
    //3.通过请求对象发起异步请求
    call.enqueue(object : CallBack<Any>() {
                override fun onSuccess(call: Call<*>?, response: BaseResponse<Any>?, data: Any?) {
                    showToast("请求成功")
                }
            })
}

没错,我们已经将第一第二步的过程搞清楚了,最后我们进入最终章,发起请求的源码解析!

2.2.2.2 发起请求!

既然我们是默认通过ExecutorCallbackCall发起请求的,那么我们只要Focus这个类就可以了吧~?
刚刚埋了个伏笔就是我们的OkHttpCall没有说明!我们看源码

    static final class ExecutorCallbackCall<T> implements Call<T> {
        //线程调度器
        final Executor callbackExecutor;
        //负责发起请求的对象
        final Call<T> delegate;
        //1.我们可以根据构造函数参数判断出,OkHttpCall才是最终负责请求的对象,而这个类只是又代理了一层而已
        ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
            this.callbackExecutor = callbackExecutor;
            this.delegate = delegate;
        }
        //2.异步请求
        public void enqueue(final Callback<T> callback) {
            Objects.requireNonNull(callback, "callback == null");
            //2.1实际上是通过OkHttpCall发送请求
            this.delegate.enqueue(new Callback<T>() {
                public void onResponse(Call<T> call, Response<T> response) {
                    //2.2 通过线程调度器切换到胡先成
                    ExecutorCallbackCall.this.callbackExecutor.execute(() -> {
                        if (ExecutorCallbackCall.this.delegate.isCanceled()) {
                            callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
                        } else {
                            callback.onResponse(ExecutorCallbackCall.this, response);
                        }

                    });
                }

                public void onFailure(Call<T> call, Throwable t) {
                 //2.2 通过线程调度器切换到主线程
                    ExecutorCallbackCall.this.callbackExecutor.execute(() -> {
                        callback.onFailure(ExecutorCallbackCall.this, t);
                    });
                }
            });
        }
        //3.同步请求直接也不装了,用OkHttpCall发送请求
        public Response<T> execute() throws IOException {
            return this.delegate.execute();
        }

    //...省略部分函数
    }

从上面我们可以得知,这个类除了切换线程就没做其他事情了,关键逻辑在OkHttpCall中,不说了 最后冲刺!

额外tips:
这个类是支持我们用我们的自定义工厂生产的,意味着Retrofit给我们提供了干预过程的机会,有相关需求的时候可以重写CallAdaperFactory来生产我们自己的CallAdapter。

2.2.2.3 OkHttpCall是怎么发起请求的!

废话不多说,OkHttpCall源码直接摊开!

final class OkHttpCall<T> implements Call<T> {
  //保存了请求数据的requestFactory
  private final RequestFactory requestFactory;
  //通过动态代理中代理函数获得的具体参数
  private final Object[] args;
  //请求工厂
  private final okhttp3.Call.Factory callFactory;
  //响应数据转换器
  private final Converter<ResponseBody, T> responseConverter;
    
//1.构造函数也对应了上方成员变量
 OkHttpCall(
      RequestFactory requestFactory,
      Object[] args,
      okhttp3.Call.Factory callFactory,
      Converter<ResponseBody, T> responseConverter) {
    this.requestFactory = requestFactory;
    this.args = args;
    this.callFactory = callFactory;
    this.responseConverter = responseConverter;
  }
  //2.异步请求
    @Override
  public void enqueue(final Callback<T> callback) {
    //省略部分合法性检测...
    okhttp3.Call call;
    Throwable failure;

    synchronized (this) {
      call = rawCall;
      if (call == null && failure == null) {
        try {
        //2.1 获取真正的Call请求对象
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          throwIfFatal(t);
          failure = creationFailure = t;
        }
      }
    }
    //省略部分合法性检测...
    //2.2 通过call对象发送请求
    call.enqueue(
        new okhttp3.Callback() {
          @Override
          public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
            Response<T> response;
            try {
            //2.3 解析返回值
              response = parseResponse(rawResponse);
            } catch (Throwable e) {
              throwIfFatal(e);
              callFailure(e);
              return;
            }
            try {
            //2.4 回调结果
              callback.onResponse(OkHttpCall.this, response);
            } catch (Throwable t) {
              throwIfFatal(t);
              t.printStackTrace(); 
            }
          }
        });
  }
}

整体请求流程算是已经出来了,但是还有两部分需要继续剖析:

  1. 生产call的函数需要看看,我们之前做的努力,怎么在这一刻开花的
  2. 解析参数这里,不能忘记了去看看响应转换器是怎么工作的

快说完了,坚持看完它!

2.2.2.4 OkHttpCall通过RequestFactory生成请求的!

从上方源码可以看出,通过createRawCall()这个函数生成出请求对象,那么我们看看源码这个方法做了啥

 private okhttp3.Call createRawCall() throws IOException {
    //1.通过callFactory.newCall(Request request)返回请求
    //2.通过requestFactory.create(args)返回Request
    okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }
  

首先我们看看requestFactory.create(args)做了啥

  okhttp3.Request create(Object[] args) throws IOException {
    //1.拿到之前的参数处理器数组
    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
    //2.将参数传入RequestBuilder中,RequestBuilder中保存的数据基本是Http请求必须有的参数,这里不展开详细说明,可自己看源码
    RequestBuilder requestBuilder =
        new RequestBuilder(
            httpMethod,
            baseUrl,
            relativeUrl,
            headers,
            contentType,
            hasBody,
            isFormEncoded,
            isMultipart);
    //3.声明一个参数集合
    List<Object> argumentList = new ArrayList<>(argumentCount);
    //4.遍历参数
    for (int p = 0; p < argumentCount; p++) {
      argumentList.add(args[p]);
      //5.调用参数处理器的apply方法,参数处理器根据自己的类型将参数保存到requestBuilder中。
      handlers[p].apply(requestBuilder, args[p]);
    }
    //6.首先通过requestBuilder.get(),结合自己保存的参数构建出OkHttp的Request.Builder对象
    //7.再为这个请求打上标签
    //8.最后通过Request.Builder的build()构建并且返回OkHttp的request对象(该对象就真的是用来请求用了)
    return requestBuilder.get().tag(Invocation.class, new Invocation(method, argumentList)).build();
  }

上面RequestBuilder如何转换成OkHttp的Request.Builde,细看RequestBuilder的构造函数大概也能了解得差不多,其实里面就是准备Http请求应有的数据。
最后通过Request.Builder的build()函数返回了一个Request,OkHttp3那边的源码就不在这里展开说了~后面有时间会再开一个篇章说。

2.2.2.5 OkHttpCall是怎么解析响应值的!

我删减了一些代码 只保留了核心的逻辑…

  Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
  //1.拿到了响应内容
    ResponseBody rawBody = rawResponse.body();
    ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
    try {
    //2.通过responseConverter转换数据类型,而responseConverter则在OkHttpCall的构造函数中传进来的,默认我们用Gson的Converter来对ResponseBody进行解析,这里也不展开说了
      T body = responseConverter.convert(catchingBody);
     //3.通过回调返回结果
      return Response.success(body, rawResponse);
    } 
  }

这样一来,整个Retrofit从初始化到请求到响应的过程就分析完了~

3. 最后

Retrofit的源码解析就到这里告一段落了,我们了解到以下几点:

  1. 为什么只声明接口,Retrofit会给我们返回对象
  2. 声明在接口上的注解、参数、参数注解、返回类型是怎么解析
  3. 发起请求的整个流程是怎么样的?
  4. 如何运用反射,写出优雅的代码
  5. 合理善用设计模式,使得代码更加健壮、易拓展、优雅
  6. 多态、封装、继承的最佳学习资料

第一次写篇幅如此长的文章,肯定有写的不好的地方,如果您希望跟我探讨,可通过[email protected] 这个邮箱找到我~我看到后会进行回复。谢谢大家

更多文章可以关注我的博客

你可能感兴趣的:(框架-原理,android,java,开发语言)