【Android】Retrofit基础源码分析

流程图

流程图

基本使用

下面的使用不涉及到添加请求适配器、返回转换器等。

返回的就是最纯正的ResponseBody

1. 创建服务端ApiInterface

interface MyApi {

    @POST("query.php")// 表示改接口使用post的方式请求
    @FormUrlEncoded// 表示content-type为:application/x-www-form-urlencoded
    fun queryByTitle(@Field("param") paramJsonStr: String, @Field("type") type: Int = 0):Call<ResponseBody>
}

关于Retrofit中的注解有哪些,作用分别是什么,推荐看这篇文章:《Android 网络框架:Retrofit2注解篇》

2.配置BaseUrl生成Retrofit对象

val baseUrl = "https://www.*.com/aabb/"
val retrofit = Retrofit.Builder()
    .baseUrl(baseUrl)
    .build()

注意:baseUrl一定要以"/"结尾,具体原因会在后面的源码中说明。

3.生成服务端ApiInterface对象

val api = retrofit.create(MyApi::class.java)

4.调用服务端ApiInterface中的方法

// 请求参数
val paramJson = JsonObject()
paramJson.addProperty("title", "黄金瞳")

val call = api.queryByTitle(paramJson.toString())

5.发起请求

// 异步请求
call.enqueue(object :Callback<ResponseBody>{
    override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
        // 请求失败
    }

    override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
        // 请求成功
        Log.d("d",response.body()?.string())
    }

})

 // 同步请求
 // 这里会出错,因为Android不允许在主线程执行网络请求
val response = call.execute()
Log.d("d",response.body()?.string())

源码分析

1. 配置BaseUrl生成Retrofit对象

首先看看Retrofit.Builder的构造函数。

Builder(Platform platform) {
  this.platform = platform;
}

public Builder() {
  this(Platform.get());
}

Builder(Retrofit retrofit) {
 ...省略代码
}

有三个构造函数,一般来说都使用的是无参构造函数,然后可以看到调用了Platform.get(),根据名字可以猜到这里是确定当前执行代码的平台。进去看看。

private static final Platform PLATFORM = findPlatform();

static Platform get() {
  return PLATFORM;
}

private static Platform findPlatform() {
  try {
    // 查看是否是Android平台
    Class.forName("android.os.Build");
    if (Build.VERSION.SDK_INT != 0) {
      return new Android();
    }
  } catch (ClassNotFoundException ignored) {
  }
  // 返回Java8平台
  return new Platform(true);
}

由于上面的例子是在Android中写的,因此这里分析的时候也可以看到返回的是一个Android对象。关于这个对象,功能很简单,就是提供了一个主线程Handler,用于将任务回调到主线程中去。

static final class Android extends Platform {
  Android() {
    super(Build.VERSION.SDK_INT >= 24);
  }

  @Override public Executor defaultCallbackExecutor() {
    // 返回了一个主线程执行器
    return new MainThreadExecutor();
  }

  static class MainThreadExecutor implements Executor {
    // 主线程handler
    private final Handler handler = new Handler(Looper.getMainLooper());

    @Override public void execute(Runnable r) {
      // 将任务提交到主线程的队列中,这样任务就可以在主线程中执行了
      // 完成了线程切换
      handler.post(r);
    }
  }
}

现在回去Retrofit.Builder中的baseUrl()方法看看是如何处理传入的String的。

public Builder baseUrl(String baseUrl) {
  Objects.requireNonNull(baseUrl, "baseUrl == null");
  return baseUrl(HttpUrl.get(baseUrl));
}

先检查了一下是否为Null,然后将String类型的url转换成HttpUrl的形式。

public Builder baseUrl(HttpUrl baseUrl) {
  Objects.requireNonNull(baseUrl, "baseUrl == null");
  List<String> pathSegments = baseUrl.pathSegments();// 这一步是将url进行拆解
  // 必须以"/"结尾
  if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
    throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
  }
  this.baseUrl = baseUrl;
  return this;
}

这里主要是对url进行拆解,并判断是否是以"/"结尾。

配置好baseUrl之后,再去看看build()方法是如何生成Retrofit对象的。

public Retrofit build() {
  // 必须要设置baseUrl,在设置的时候还会检查baseUrl是不是以"/"结尾
  if (baseUrl == null) {
    throw new IllegalStateException("Base URL required.");
  }

  // 所谓的callFactory 就是okHttpClient 可以看做为一个浏览器
  okhttp3.Call.Factory callFactory = this.callFactory;
  if (callFactory == null) {
    callFactory = new OkHttpClient();
  }

  // 这里大部分情况下是没设置的,因此这里的platform基本为Android()
  // 返回的默认执行器也是主线程执行器,用于将runnable用主线程handler执行
  Executor callbackExecutor = this.callbackExecutor;
  if (callbackExecutor == null) {
    callbackExecutor = platform.defaultCallbackExecutor();
  }

  // Make a defensive copy of the adapters and add the default Call adapter.
  // 翻译:制作适配器的防御副本,并添加默认的呼叫适配器。
  // 这里优先添加自己配置的CallAdapterFactory,然后再在末尾添加一个默认的
  List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
  callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

  // Make a defensive copy of the converters.
  // 制作防御者的副本。
  List<Converter.Factory> converterFactories = new ArrayList<>(
      1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());

  // Add the built-in converter factory first. This prevents overriding its behavior but also
  // ensures correct behavior when using converters that consume all types.
  // 翻译:首先添加内置转换器工厂。 这可以防止覆盖其行为,也可以确保使用使用所有类型的转换器时的正确行为。
  converterFactories.add(new BuiltInConverters());
  converterFactories.addAll(this.converterFactories);
  converterFactories.addAll(platform.defaultConverterFactories());

  return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
      unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}

这一步主要涉及这几个变量的赋值:callFactorycallbackExecutorcallAdapterFactoriesconverterFactories

需要注意的是,callAdapterFactoriesconverterFactories中,index越小的元素是越先被获取到的。另外可以看到,对于callAdapterFactories是优先加入用户配置的然后才加入默认的;而converterFactories则相反,它先加入了一个默认的,然后才加入用户配置的。

callbackExecutor是由platform提供的,根据上面的源码可以看到,这里返回了一个MainThreadExecutor。用于将非主线程任务回调到主线程中。

至此,Retrofit对象就创建好了,很明显使用了设计模式-建造者模式。

2. 生成服务端ApiInterface对象

去到Retrofit中的create()方法看看。

public <T> T create(final Class<T> service) {
    validateServiceInterface(service);// 验证接口,以及判断是否要提前解析注解,默认为false

    /**
     * 动态代理的方式创建interface对象
     * @param service.getClassLoader() 类加载器
     * @param new Class<?>[] { service } 要实现的类接口
     * @param new InvocationHandler() 处理调用方法的InvocationHandler
     * @return 返回接口中声明的对象类型
     */
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          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 {
              ...省略部分代码
                  
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          }
        });
  }

这里的话,使用到了代理模式中的动态代理。关于代理模式,可以看看这篇文章:《【Java】小例子巧妙理解代理模式》。这样,服务端ApiInterface对象也创建好了,注意这里是创建的代理对象。

下面,看看调用其中的方法时,源码是如何执行的

3. 调用服务端ApiInterface中的方法

了解动态代理的话应该知道,当调用代理对象的方法的时候,实际上会调用到InvocationHandler中的invoke()

那么这里的话,就是调用到上面create()中的invoke()方法了。

如果略过第一行的校验代码的话,可以看到整个方法关键之处在于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 = ServiceMethod.parseAnnotations(this, method);
      serviceMethodCache.put(method, result);
    }
  }
  return result;
}

该方法会返回一个ServiceMethod对象,首先从缓存中查找是否有已经生成的,如果没有则调用ServiceMethod.parseAnnotations()去生成一个,再加入到缓存中。

那么继续看看这个生成ServiceMethod的方法。

static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
  /**这里解析注解*/
  RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

  ...省略部分校验代码

  return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}

首先生成了一个RequestFactory对象,根据名字,猜测应该是用于构建请求的工厂。

然后调用HttpServiceMethod中的parseAnnotations()方法。注意,根据idea查看,HttpServiceMethodServiceMethod这个类的唯一子类。小技巧:idea系列的代码编辑器可以通过alt+h来查看当前类的子类以及父类。

那么根据先后顺序,先看看RequestFactory是如何生成的,进入RequestFactory.parseAnnotations()

3.1 构建RequestFactory

static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
  return new Builder(retrofit, method).build();
}

又是熟悉的Builder类,建造者模式。

Builder(Retrofit retrofit, Method method) {
  this.retrofit = retrofit;
  this.method = method;
  this.methodAnnotations = method.getAnnotations();// 获取方法注解
  this.parameterTypes = method.getGenericParameterTypes();// 获取参数值类型
  this.parameterAnnotationsArray = method.getParameterAnnotations();// 获取参数注解
}

这里的Method就是上面的ApiInterface中的方法。即MyApi.queryByTitle()

可以看到,在构造函数中,根据传入的方法,获取到了方法注解、返回值类型、参数注解。

然后看看建造者模式中最精华的build()方法。

RequestFactory build() {
  // 解析方法注解
  for (Annotation annotation : methodAnnotations) {
    parseMethodAnnotation(annotation);
  }

  ...省略部分校验代码

  // 解析参数注解
  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);
  }

  ...省略部分校验代码

  return new RequestFactory(this);
}

很显然,这里在忽略了校验代码之后,主要就是将ApiInterface中的方法注解和参数注解解析出来。

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

看上去很头疼,这么多的if else if语句。实际上的逻辑其实非常简单。

首先,判断注解是否属于请求方式类型的注解,如果是的话则调用parseHttpMethodAndPath()解析出请求方式以及请求参数。

其次,如果是Headers注解的话,就调用parseHeaders()将要设置的请求头解析出来。

最后,如果是关于设置content-type的注解的话,则将对应的标志位设置为true

老规矩,根据代码顺序,看看调用的方法中的逻辑。

3.1.1.1 请求方式类注解解析

首先是parseHttpMethodAndPath()

private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
  if (this.httpMethod != null) {
    // 之前已经定义过请求方式了,再重新定义就抛出异常
    throw methodError(method, "Only one HTTP method is allowed. Found: %s and %s.",
        this.httpMethod, httpMethod);
  }
  this.httpMethod = httpMethod;
  this.hasBody = hasBody;

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

  // Get the relative URL path and existing query string, if present.
  // 翻译:获取相对URL路径和现有查询字符串(如果存在)。
  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()) {
      // 翻译:URL查询字符串“%s”不得包含replace块。对于动态查询参数,请使用@Query
      throw methodError(method, "URL query string \"%s\" must not have replace block. "
          + "For dynamic query parameters use @Query.", queryParams);
    }
  }

  this.relativeUrl = value;
  this.relativeUrlParamNames = parsePathParameters(value);
}

首先判断是否已经定义了请求方式了,然后又一个正则判断,用于查看相对url中是否存在动态查询参数。

如果有查询字符串,如:“index.php?key=value&key1={param1}”,那么这里就会抛出异常,因为查询字符串应该使用@Query这个注解。

最后调用parsePathParameters()将请求参数解析出来。这个方法就不在这里贴代码了,有兴趣的可以自行去查看。逻辑很简单,也是使用正则表达式进行匹配的。

3.1.1.2 请求头注解解析

现在回到前面,再看看parseHeaders()这个方法是如何把@Headers注解解析出来的。

private Headers parseHeaders(String[] headers) {
  Headers.Builder builder = new Headers.Builder();
  for (String header : headers) {
    int colon = header.indexOf(':');// 第一个冒号":"的位置
    if (colon == -1 || colon == 0 || colon == header.length() - 1) {
      // 冒号不存在、冒号在开头、冒号在结尾都会出错
      throw methodError(method,
          "@Headers value must be in the form \"Name: Value\". Found: \"%s\"", header);
    }
    String headerName = header.substring(0, colon);
    String headerValue = header.substring(colon + 1).trim();

    // 针对content-type的特殊处理,生成MediaType
    if ("Content-Type".equalsIgnoreCase(headerName)) {
      try {
        contentType = MediaType.get(headerValue);
      } catch (IllegalArgumentException e) {
        throw methodError(method, e, "Malformed content type: %s", headerValue);
      }
    } else {
      builder.add(headerName, headerValue);
    }
  }
  return builder.build();
}

整个方法分为两个部分,第一部分利用冒号":"所在的位置,将请求头分割为headerNameheaderValue

第二部分是针对content-type的特殊处理,MediaType类型,注意:content-type这个请求头并没有加入到builder中,而是特地用一个contentType变量保存起来了。

OK,到这里,关于方法中的注解就解析完成了,下面就要准备去解析参数中的注解了。

3.1.2 解析参数注解
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 (annotationAction == null) {
        continue;
      }

      if (result != null) {
        // 多个参数注解将报错
        throw parameterError(method, p,
            "Multiple Retrofit annotations found, only one allowed.");
      }

      result = annotationAction;
    }
  }

  ...省略校验代码

  return result;
}

这个方法主要是循环注解,为每个参数注解都生成一个ParameterHandler对象。

关键方法是parseParameterAnnotation()

这个方法比前面解析方法注解那个方法还要长,所以这里只针对性的挑选部分代码贴出来。

private ParameterHandler<?> parseParameterAnnotation(
    int p, Type type, Annotation[] annotations, Annotation annotation) {
  if (annotation instanceof Url) {
    validateResolvableType(p, type);
      ...省略部分校验代码

    gotUrl = true;

    // @Url这个注解所标注的参数类型只能是:
    // HttpUrl,String,URI,Uri(Android平台特有)
    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);
    ...省略部分校验代码
    gotPath = true;

    Path path = (Path) annotation;
    String name = path.value();
    validatePathName(p, name);// 验证是否和url中声明的动态参数一致

    Converter<?, String> converter = retrofit.stringConverter(type, annotations);
    return new ParameterHandler.Path<>(method, p, name, converter, path.encoded());

  }
  else if (annotation instanceof Query) {
    validateResolvableType(p, type);
    Query query = (Query) annotation;
    String name = query.value();
    boolean encoded = query.encoded();// 是否需要urlEncode,默认false

    Class<?> rawParameterType = Utils.getRawType(type);
    gotQuery = true;
      ...这里简化了代码
   return new ParameterHandler.Query<>(name, converter, encoded);

  }
  ...省略部分代码

  return null; // Not a Retrofit annotation.
}

即使只贴了部分代码,整个方法依然很长,但是整体逻辑很简单,就是根据注解类型返回对应的ParameterHandler即可。

那么ParameterHandler到底有什么作用呢?进去这个类看看就知道到了。

abstract class ParameterHandler<T> 

是一个抽象类,那么老规矩,ctrl+h查看一下它的子类。

parameterHandler

可以看到有很多子类,而且注意一下可以发现,和注解是一一对应的,每个注解都对应了一个ParameterHandler,这里的话就随便选一个进行分析吧。

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

  Field(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 fieldValue = valueConverter.convert(value);
    if (fieldValue == null) return; // Skip null converted values

    builder.addFormField(name, fieldValue, encoded);
  }
}

关键方法是apply(),该方法接收两个参数,一个是RequestBuilder,一个是value

首先将value用转换器转换成String类型,然后将其赋值给RequestBuilder。看过这么多建造者模式,看到这个很自然就知道,这里就是将参数注解解析后的值传给Request

到这里,生成RequestFactory就完成了,然后回到前面,继续看看后面的代码。

static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
  /**这里解析注解*/
  RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

  ...省略部分校验代码

  return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}

3.2 构建HttpServiceMethod

下面就是看看这个HttpServiceMethod.parseAnnotations()方法干了什么了。

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) {
    ...kotlon协程相关
  } else {
    adapterType = method.getGenericReturnType();
  }

  /**创建请求适配器*/
  CallAdapter<ResponseT, ReturnT> callAdapter =
      createCallAdapter(retrofit, method, adapterType, annotations);

  /**返回值类型,在Interface的方法中自己定义的*/
  Type responseType = callAdapter.responseType();

  // 校验返回值类型是否合法
  ...省略部分校验代码

  /**创建返回值转换器*/
  Converter<ResponseBody, ResponseT> responseConverter =
      createResponseConverter(retrofit, method, responseType);

  okhttp3.Call.Factory callFactory = retrofit.callFactory;// 这里就是配置okHttpClient

  // 选择返回哪种HttpServiceMethod
  if (!isKotlinSuspendFunction) {
    return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
  } else if (continuationWantsResponse) {
    //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
    // 翻译:noinspection未经检查的Kotlin编译器保证ReturnT为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);
  }
}

这个方法主要是获取返回值转换器、请求适配器。

虽然获取的时候,函数名为create,然而实际是上调用到了Retrofit中的方法去获取之前配置的(没有配置则选择默认的)。

从代码最后面的部分可以看到,返回的类型有三种,然而实际上只需要关注CallAdapted这个即可,因为另外的两个是给kotlin的协程使用的。

OK,到这里,ServiceMethod也创建好了,回到最初的原点,看看获取到ServiceMethod之后的代码是怎么样的。

  public <T> T create(final Class<T> service) {
    ...省略部分校验代码
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          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 {
            ...省略部分校验代码
            // 当调用生成的ApiInterface中的方法的时候,实际上回调用到这里
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          }
        });
  }

可以看到,当获取到ServiceMethod对象之后,立刻调用了它的invoke()方法,那么回到前面的源码,看看invoke()中又进行了什么操作。

3.3 构建OkHttpCall

@Override
final @Nullable ReturnT invoke(Object[] args) {
  // 创建实际请求对象
  Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
  return adapt(call, args);
}

这里创建了一个OkHttpCall,顾名思义,它就是用来发起请求的最终对象了。

另外还调用adapt(),因为之前说了只关注CallAdapted这个子类,所以这里直接分析CallAdaptedadapt()方法。

@Override
protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
  /**先调用callAdapter.Factory.get,获取到callAdapter
   * 默认情况下是DefaultCallAdapterFactory*/
  return callAdapter.adapt(call);
}

很简单,返回了一个由callAdapter生成的对象。

由于本次源码分析没有引入其他的callAdapter,因此这里的就是Retrofit默认的callAdapter了。那回到Retrofit.Builder.build()方法中,看看默认的callAdapter是谁?

List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

调用了platform.defaultCallAdapterFactories(),那么跟进看看方法里面是什么样子的。

List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
    @Nullable Executor callbackExecutor) {
  DefaultCallAdapterFactory executorFactory = new DefaultCallAdapterFactory(callbackExecutor);
  return hasJava8Types
      ? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
      : singletonList(executorFactory);
}

实例化了一个DefaultCallAdapterFactory

继续跟进,在它的get()方法中,发现返回了一个callAdapter

@Override
public @Nullable CallAdapter<?, ?> get(
...省略部分代码

    final Executor executor = Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
        ? null
        : callbackExecutor;
    
  return new CallAdapter<Object, Call<?>>() {
    @Override public Type responseType() {
      return responseType;
    }

    @Override public Call<Object> adapt(Call<Object> call) {
      return executor == null
          ? call
          : new ExecutorCallbackCall<>(executor, call);
    }
  };
}

首先判断了下是否有@SkipCallbackExecutor这个注解,如果有这个注解的话就不进过ExecutroCallback了,直接返回当前的call对象。否则则返回一个ExecutorCallbackCall

至此,调用服务端ApiInterface中的方法也分析完成了,这一步主要就是将各种注解解析出来,然后拼接成一个RequestBuilder对象,最后生成一个call的子类ExecutorCallbackCall

4. 发起请求

根据前面的分析,可以很明显的知道,其实调用发起请求的对象是ExecutorCallbackCall

那么下面就来分别看看,它的异步请求和同步请求是如何执行的。

4.1 异步请求

@Override public void enqueue(final Callback<T> callback) {
  Objects.requireNonNull(callback, "callback == null");

  // 实际调用的是OkHttpCall的方法
  delegate.enqueue(new Callback<T>() {
    @Override public void onResponse(Call<T> call, final Response<T> response) {
      callbackExecutor.execute(() -> {
        // 这里就回到主线程了
        if (delegate.isCanceled()) {
          // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
          callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
        } else {
          callback.onResponse(ExecutorCallbackCall.this, response);
        }
      });
    }

    @Override public void onFailure(Call<T> call, final Throwable t) {
      callbackExecutor.execute(() -> callback.onFailure(ExecutorCallbackCall.this, t));
    }
  });
}

这里可以看到,只是调用了delegate.enqueue(),并在回调函数中使用callbackExecutor执行。

通过前面的分析,可以知道callbackExecutor.execute()只是将任务交给主线程Handler而已,因此这里不再过多关注。更多的关注到delegate这个对象。

在前面构建OkHttpCall这一步的时候,传入到CallAdapter中的call是一个OkHttpCall。然后该call一直被原封不动的继续向下传递,一直传递到了ExecutorCallbackCall。那么显然这里的delegate就是前面构建的OkHttpCall了。既然如此delegate.enqueue(),也就是OkHttpCall.enqueue()

@Override public void enqueue(final Callback<T> callback) {
  Objects.requireNonNull(callback, "callback == null");

  okhttp3.Call call;
  Throwable failure;

  synchronized (this) {
    if (executed) throw new IllegalStateException("Already executed.");
    executed = true;
...
        call = rawCall = createRawCall();
...

 ...

  call.enqueue(new okhttp3.Callback() {
    @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
      Response<T> response;
      ...
          // 解析response
        response = parseResponse(rawResponse);
      ...
        callback.onResponse(OkHttpCall.this, response);
      ...
    }

    @Override public void onFailure(okhttp3.Call call, IOException e) {
      callFailure(e);
    }

    private void callFailure(Throwable e) {
      ...省略部分代码
    }
  });
}

到这里,如果以前用过OkHttp的话,就很熟悉啦。这不就是OkHttp的异步请求吗?

首先将Retrofit.Call转换成OkHttp.Call,调用了createRawCall()这个方法。

private okhttp3.Call createRawCall() throws IOException {
  okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
  if (call == null) {
    throw new NullPointerException("Call.Factory returned null.");
  }
  return call;
}

这个方法就利用到了前面构建的RequestFactory啦。将其变成了OkHttp.Request,这样OkHttp才能够发起正确的请求。

请求成功后,调用parseResponse()将OkHttp返回的响应结果解析成用户想要的返回结果。这个方法留在后面分析。

现在,来看看同步请求。

4.2 同步请求

@Override public Response<T> execute() throws IOException {
  okhttp3.Call call;

  synchronized (this) {
    ...省略部分校验代码

    call = rawCall;
    if (call == null) {
      try {
        call = rawCall = createRawCall();
      } catch (IOException | RuntimeException | Error e) {
        throwIfFatal(e); //  Do not assign a fatal error to creationFailure.
        creationFailure = e;
        throw e;
      }
    }
  }

  if (canceled) {
    call.cancel();
  }

  return parseResponse(call.execute());
}

同步请求就更简单了,没啥可说的了,转换后的OkHttp.Call直接调用execute()方法获取响应结果,然后同样调用parseResponse()去将响应结果转换成用户想要的形式。

5. 解析响应结果

Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
  ResponseBody rawBody = rawResponse.body();

  // Remove the body's source (the only stateful object) so we can pass the response along.
  // 翻译:删除主体的源(唯一的有状态对象),以便我们传递响应。
  rawResponse = rawResponse.newBuilder()
      .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
      .build();

  int code = rawResponse.code();
  if (code < 200 || code >= 300) {
    try {
      // Buffer the entire body to avoid future I/O.
      // 翻译:缓冲整个主体以避免将来的I / O。
      ResponseBody bufferedBody = Utils.buffer(rawBody);
      return Response.error(bufferedBody, rawResponse);
    } finally {
      rawBody.close();
    }
  }

  if (code == 204 || code == 205) {
    // 204:请求没有数据返回,但是头信息有用。用户代理(浏览器)会更新缓存的头信息
    // 205:告诉用户代理(浏览器)重置发送该请求的文档。
    rawBody.close();
    return Response.success(null, rawResponse);
  }

  ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
  try {
    // 大部分情况下,不设置转换器的时候,返回类型都是ResponseBody
    // 这个时候会使用到BuiltInConverters中的StreamingResponseBodyConverter或BufferingResponseBodyConverter
    T body = responseConverter.convert(catchingBody);
    return Response.success(body, rawResponse);
  } catch (RuntimeException e) {
    // If the underlying source threw an exception, propagate that rather than indicating it was
    // a runtime exception.
    catchingBody.throwIfCaught();
    throw e;
  }
}

这次没有省略代码了,因为这个方法里面的大部分代码都很有必要知道。

首先克隆了一个原响应,这一步如果用过OkHttp原生的话应该知道为什么。因为OkHttp原响应只允许你读取一次,读取完之后就关闭了,如果你还想再去读的话,则会抛出异常java.lang.IllegalStateException: closed

然后根据响应状态码进行不同的处理,这里的话只分析一下正常的流程。

调用了一个responseConverter对响应进行转换。那么这个responseConverter又从哪里来呢?

回想之前在配置Retrofit的时候,似乎在Retrofit.Builder中有涉及到。

converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);
converterFactories.addAll(platform.defaultConverterFactories());

由于没有自己配置转换器,因此这里只有一个BuiltInConverters和平台默认的转换器。而平台默认转换器只有在**Java8和Android API 24+**才有,而且只能转换成Optional的形式,所以这里很显然会调用到BuiltInConverters中去进行转换。

进入BuiltInConverters中可以看到有这么几个内置的转换器,分别是:

  1. BufferingResponseBodyConverter

  2. RequestBodyConverter

  3. StreamingResponseBodyConverter

  4. ToStringConverter

  5. UnitResponseBodyConverter

  6. VoidResponseBodyConverter

分别查看后发现,只有BufferingResponseBodyConverterStreamingResponseBodyConverter这两个转换器是转换成ResponseBody的。

它们两者的区别主要是这样的。BufferingResponseBodyConverter会将返回结果写入到内存中,如果响应结果过大的话就会发生内存溢出异常了,比如下载文件的时候。而StreamingResponseBodyConverter则不会将结果写入到内存。它们两个之间如何选用,是根据是否使用了@Streaming这个注释来判断的。

可以在BuiltInConverters.responseBodyConverter()中看到。

@Override public @Nullable Converter<ResponseBody, ?> responseBodyConverter(
    Type type, Annotation[] annotations, Retrofit retrofit) {
  if (type == ResponseBody.class) {
      // 根据是否有@Streaming注解来觉得使用哪个转换器
    return Utils.isAnnotationPresent(annotations, Streaming.class)
        ? StreamingResponseBodyConverter.INSTANCE
        : BufferingResponseBodyConverter.INSTANCE;
  }
  if (type == Void.class) {
    return VoidResponseBodyConverter.INSTANCE;
  }
  if (checkForKotlinUnit) {
    try {
      if (type == Unit.class) {
        return UnitResponseBodyConverter.INSTANCE;
      }
    } catch (NoClassDefFoundError ignored) {
      checkForKotlinUnit = false;
    }
  }
  return null;
}

总结

基本源码分析到这里就结束了。当然在日常的开发中,经常会将Retrofit和RxJava、Gson、Kotlin协程等一起配合使用。这就涉及到请求与响应是如何进行转换的,这是这篇文章没有分析到的。

通过基本的分析,可以看到Retrofit使用了大量的设计模式,而这些设计模式也让它能够很快速的去适配其他框架。比如经典的适配Gson以及RxJava,就是因为它使用的策略模式。Retrofit本身并不负责转换请求以及响应,都交给了转换器去执行。而对用户而言,只需配置自己需要的转换器,即可轻松的完成转换,而不需要任何其他的代价。

另外,Retrofit本质上其实只是对OkHttp进行了封装,使用过OkHttp原生的人都知道,如果不进行封装的话,每次发起请求都要写一大堆的重复代码。如果有兴趣看看OKHttp的源码,可以关注我的这篇文章:《【Android】OkHttp系列目录》

发布了24 篇原创文章 · 获赞 7 · 访问量 8676

猜你喜欢

转载自blog.csdn.net/d745282469/article/details/104639538