Android萌新必看:读一遍Retrofit源码,这里全是细节!

Android萌新必看:读一遍Retrofit源码,这里全是细节!

阅读Retrofit源码可以说是Android开发者必须要做的一件事情了,在这里给大家分享一篇Retrofit源码的技术文,希望对刚接触Android不久的朋友学习和工作上有所帮助。
原文地址:https://juejin.cn/post/6955856064969441317
PS:大家如果对Android开发感兴趣,或者想要进阶学习,可以去我的【我的GitHub】看看,里面有很多Android技术的学习笔记整理,全部都是无偿分享的,希望对大家有帮助。

作为Android开发,基本上没人不知道Retrofit,也或多或少的看过源码。

一般看源码的流程就是从整体的大结构上去看,而不去钻太深的细节,等了解整体的设计思想后,再去进行细节的探索。

然而,很多人都是做了前面的步骤而省去了细节的探索,我就是经常这样。这就会导致一种后果,就是只是明白了它的大体结构和设计思想。虽然这对于我们学习就已经够了,毕竟我们学的就是设计思想设计理念,但是对于我们的使用却是不够。

@GET("xxx")
fun example(): Call<ResponseBody>

@GET("xx")
suspend fun example(): ResponseBody

@GET("xx")
suspend fun example(): Response<ResponseBody>

@GET("xxx")
fun example(): CompleteFuture<Response>

复制代码

就像上面的代码,都是Retrofit默认支持的几种写法。我们常用的基本都是前两种,后面的可能就了解的比较少,下面就从细节一点一点分析它为什么可以这样写,以及还有什么写法。

准备工作

这里使用的Retrofit 2.9.0版本

// build.gradle
implementation 'com.squareup.retrofit2:retrofit:2.9.0'

// 创建service接口,声明网络请求。这个工作不是必要工作,只是为了后续的讲解方便
// Api.kt
interface Api {
    @GET("/hotkey/json")
    fun example(): Call<ResponseBody>
}
复制代码

因为源码细节比较多,我们很容易看着看着就迷失在代码中,所以需要做一个思维导图来记录下当前的位置。

首先Retrofit的使用比较简单,首先创建Retofit实例,使用Retrofit实例去创建Service,然后调用Service的接口即可。接下来就是根据这个过程一步一步去追踪即可。

Android萌新必看:读一遍Retrofit源码,这里全是细节!

创建Retrofit实例

val retrofit = Retrofit.Builder()
    .baseUrl("https://wanandroid.com")
    .build()

这是使用Retrofit的第一步,也就是先创建一个Retrofit实例,从创建的方式也可以知道采用的Builder模式,通过一系列的方法设置参数,然后去build创建实例。

为什么要采用这种设计模式呢,有什么好处呢?首先我们知道Builder模式是为了将复杂对象的构建进行拆离的,也就是可能要构建的对象参数很多,如果直接创建的话,大量的参数会是我们非常困惑。通过这种方式,可以将每一个参数进行拆分出去,能够单独设置,并且还能对未设置的参数赋上默认值以及能够控制最终构建的一个顺序。

对于这种Builder模式,我们不要先去看Builder的每一个方法,而是直接去找最后一个build方法,然后根据它最终设置的每一个参数再去看对应的方法,这样才能对每个方法的作用都有一个比较清晰的理解。

// Retrofit.java
public Retrofit build() {
    //1, baseUrl必填的
    if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
    }
    //2, callFactory,用于创建OkHttp.Call实例
    okhttp3.Call.Factory callFactory = this.callFactory;
    if (callFactory == null) {
      callFactory = new OkHttpClient();
    }
    //3, 回调线程池
    Executor callbackExecutor = this.callbackExecutor;
    if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
    }

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

    //5, converterFactory
    List<Converter.Factory> converterFactories = new ArrayList<>(
         1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());

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

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

build方法中可以看出,在最后new Retrofit的时候传递了6个参数,这6个参数也就是Builder所做的工作。同样的,先记下来,然后再对每一个参数进行分析。

Android萌新必看:读一遍Retrofit源码,这里全是细节!

设置baseUrl

build方法中,baseUrl若是为空,则会抛出异常。这个参数最终会被用来拼接实际的请求url,所以要求是必须要设置的。baseUrl虽然提供了三个重载方法,但最后都是调用了baseUrl(HttpUrl)方法。

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

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

public Builder baseUrl(HttpUrl baseUrl) {
    Objects.requireNonNull(baseUrl, "baseUrl == null");
    List<String> pathSegments = baseUrl.pathSegments();
    // 若是有url中含有path的话,必须以/结尾
    if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
        throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
    }
    this.baseUrl = baseUrl;
    return this;
}

可以看到,我们可以使用三种不同类型的URL。当然,通常情况下,我们都是使用的String类型的URL。这里有一点值得注意,对于url的格式也是有要求的,要求如下:

1,baseUrl不为空
2,若是带有路径的话,则不能以 / 结尾,如下
    错误:https://www.wanandroid.com/user
    正确:https://www.wanandroid.com/user/
3,若是不带路径的话,则没有要求
    https://www.wanandroid.com
    https://www.wanandroid.com/

所以一般情况,我们设置的baseUrl全部都以/结尾即可。
复制代码

设置callFactory

什么是CallFactory呢,从名字可以看出,他是用来创建Call的一个工厂类,而创建的Call被用来进行实际的网络请求,若是没有设置的话则默认去创建一个OkhttpClient,所以从这里也可以看出Retrofit的网络请求默认是通过OkHttp去实现的。在Builder中,一共提供了两个方法来设置callFactory。一个参数是Fatory接口,一个参数是OkHttpClient

public Builder callFactory(okhttp3.Call.Factory factory) {
    this.callFactory = Objects.requireNonNull(factory, "factory == null");
    return this;
}

public Builder client(OkHttpClient client) {
    return callFactory(Objects.requireNonNull(client, "client == null"));
}

而第一个方法比较简单,其参数是okhttp3.Call.Factory接口。正因为它的参数是一个接口,所以我们可以去实现自己的Factory类使用,因此,若是不想使用OkHttp的话,则可以通过这个方法去设置属于自己的Factory

当然,现在最流行的网络框架还是OkHttp,因此还提供了一个方法client,其参数是OkHttpClientOkHttpClient也是Factory的一个子类,之所以提供这个方法,是为了我们的使用方便。我们可以创建一个OkHttpClient实例,然后进行一些网络配置,如添加拦截器等,然后通过这个方法去设置。因为其参数是OkHttpClient,我们可以很容易发现这个方法。而若是只提供一个callFactory方法的话,当然可以是可以的,但是要求我们必须知道OkHttpClientFactory的一个子类,但是可能会让一些不熟悉OkHttp的一些人感到疑惑吧。

设置callbackExecutor

Executor我们知道,一般都是用来指线程池。而这里的参数叫做callbackExecutor,也就是回调的线程池,用来执行回调方法的一个线程池。我们先看一下下面的例子:

retrofit.create(Api::class.java)
    .example()
    .enqueue(object : Callback<ResponseBody?> {
        override fun onResponse(call: Call<ResponseBody?>, response: retrofit2.Response<ResponseBody?>) {
            println("success: ${Thread.currentThread().name}")
        }
        override fun onFailure(call: Call<ResponseBody?>, t: Throwable) {
            t.printStackTrace()
        }
    })

这是调用网络请求的一个方法,其中enqueue方法传入了一个callback,用来回调网络请求成功的结果或者请求失败的结果。而回调接口的onResponseonFailure方法最终都是会被放入到callbackExecutor中去执行的,上面的例子最终会输出:success:main,为什么呢?我们并没有设置callbackExecutor啊。

再回到Retrofit.Builder#build方法中,会发现若是没有手动设置callbackExecutor的话,则会通过platform.defaultCallbackExecutor()去获取默认的Executor。而这里的platform是Platform的子类Android,这里platform的获取会调用一系列的方法,最终返回Android的实例。这里只贴一下关键代码了。MainThreadExecutor

// Platform.java
private static Platform findPlatform() {
    return "Dalvik".equals(System.getProperty("java.vm.name"))
        ? new Android()
        : new Platform(true);
  }

static final class Android extends Platform {
    Android() {
    // 注意,这个参数设置的是hasJava8Types属性
      super(Build.VERSION.SDK_INT >= 24);
    }

    @Override
    public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }
    ...
}

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

    @Override
    public void execute(Runnable r) {
        handler.post(r);
    }
}

所以看一下MainThreadExecutor的实现非常简单,就是通过创建一个Handler,然后将事件通过Handler发送到主线程中去执行。这也就是为什么前面的那个示例代码中会输出main

设置callAdapterFactories

callAdapterFactories是一个List集合,其内部存放着多个CallAdapter.Factory的实例。它是用来生产CallAdapter的,而CallAdapter是请求适配器,在Retrofit中,网络请求的实体就是OkHttpCall。而CallAdapter就是对它进行处理,将它变成更加方便我们使用的形式。

在build方法中,callAdapterFactories分为两个部分,一个是this.callAdapterFactories,一个是platform.defaultCallAdapterFactories。其中前者是我们通过方法addCallAdapterFactory手动添加的,后者是platform中默认的。

// Retrofit.java

// 我们通过这个方法去设置自己的CallAdapterFactory
public Builder addConverterFactory(Converter.Factory factory) {
    converterFactories.add(Objects.requireNonNull(factory, "factory == null"));
    return this;
}

public Retrofit build() {
    ...
    List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
    callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
    ...
}

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

因为这里我们没有主动去添加CallAdapterFactory,所以这个集合中只有默认的Factory

首先实例化了一个DefaultCallAdapterFactory,然后若是支持java8,还会添加一个CompletableFutureCallAdapter。如果仔细看前面的分析的话,会发现在Platform的子类Android中的构造方法中,传递的是Build.VERSION.SDK_INT >= 24。也就是说,当版本号不小于24(Android 7.0)的时候,hasJava8Types是true,也就是在7.0之后是支持java8的。

所以可以看到,默认一共添加了两个callAdapterFactory,另外注意添加是有顺序的,至于这两个Factory的作用,后面也会提到的。

设置converterFactories

converterFactories是用于产生ConverterAdapter,retrofit中各种转换都是要用到它,从请求参数到注解参数,再到请求结果的转换,都是通过ConverterAdapter来转换的。比如我们接口请求方法的参数是一个对象,那么我们就可以通过ConverterAdapter将它转换成json,从而更方便网络请求。比如实际的网络请求结果是ResponseBody,而实际的返回结果是通过String存储的,那么我们还可以定义ConverterAdapter实现将返回结果转换成对象来使用。

// Retrofit.java
public Retrofit build() {
    ...
    List<Converter.Factory> converterFactories = new ArrayList<>(
        1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());

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

可以看到,这个集合先添加了BuiltInConverters,然后才是this.converterFactories,最后是一个platform的默认ConverterFactory,从下面的代码可以看到,也是在版本至少24的时候才会添加一个OptionalConverterFactory。注意这里也是有顺序的。

List<? extends Converter.Factory> defaultConverterFactories() {
    return hasJava8Types ? singletonList(OptionalConverterFactory.INSTANCE) : emptyList();
}

设置validateEagerly

是一个boolean值,通过方法validateEagerly(boolean)进行设置,默认为false。

该属性是用来控制是否提前加载接口,Retrofit是通过动态代理来解析接口方法并转换成一个网络请求方法实例ServiceMethod,这一步骤是用到了反射比较耗时。

因此为了避免卡顿,retrofit在接口未使用的时候是不会去加载它的,只有在第一次调用这个方法的时候,才会去解析方法并生成相应的ServiceMethod并且存在缓存区避免多次加载。这样的实现虽然可以减轻反射的影响,但是,若是接口方法写的有问题的话,我们也不能立即发现,而是在运行到这个方法的时候才会发现。这对于开发而言是不利的,因此加了这个参数validateEagerly。当这个值设置为true的时候,只要创建了代理类,就会去解析加载所有的接口方法,从而让可能会出现的问题提前暴露。

但是这样当方法数比较多的话,可能会消耗大量时间从而造成卡顿。因此,我们一般都是在开发环境的时候将他设置为true,而正式环境设置为false。

创建Retrofit实例小结

  1. 设置baseUrl,非空且不以/结尾
  2. 设置callFactory,默认为OkHttpClient
  3. 设置callbackExecutor,默认使用Handler实现切换主线程
  4. 在原来的基础上添加DefaultCallAdapterFactory和CompleteFutureCallAdapterFactory
  5. 在原来的基础上添加BuildInConverterFactory和OptionalConverterFactory
  6. 设置validateEagerly

Android萌新必看:读一遍Retrofit源码,这里全是细节!

创建动态代理对象

前面分析的是创建Retrofit的流程,涉及到了六大参数,我们也分别了解了这六个参数的作用以及如何设置的。接下来是下一个流程,也就是创建service实例,从下面的代码上大体扫一眼就能看出来实际返回的是一个代理对象。

// 通过这个create方法创建实例
val api = retrofit.create(Api::class.java)

// Retrofit.java
public <T> T create(final Class<T> service) {
    validateServiceInterface(service);
    return (T)Proxy.newProxyInstance(...);
}
// 这一步是创建代理对象前调用的
private void validateServiceInterface(Class<?> service) {
    // 要求service必须是接口类
    if (!service.isInterface()) {
      throw new IllegalArgumentException("API declarations must be interfaces.");
    }
    // 同时,当前接口及其父接口不能有泛型参数
    Deque<Class<?>> check = new ArrayDeque<>(1);
    check.add(service);
    while (!check.isEmpty()) {
      Class<?> candidate = check.removeFirst();
      if (candidate.getTypeParameters().length != 0) {
        ...
        throw new IllegalArgumentException(message.toString());
      }
      Collections.addAll(check, candidate.getInterfaces());
    }

    // 加载每个方法
    if (validateEagerly) {
      Platform platform = Platform.get();
      for (Method method : service.getDeclaredMethods()) {
        // 非default方法和非静态方法才会去加载
        if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {
          loadServiceMethod(method);
        }
      }
    }
}

在返回代理对象前,还有一个方法validateServiceInterface。它一共做了三件事,首先判断要创建的service只能是接口类,这个比较简单。第二件事是使用了一个队列,来遍历每一个父类(这个过程与树的广度优先算法是一样的),并要求每个类都不能包含泛型参数。最后是判读是否有设置validateEagerly参数,若设置过的话,则会去加载所有的方法。

其中loadServiceMethod方法就是去解析方法,并生成ServiceMethod的过程,这里先不去分析,因为后面还会有他的戏份,到那时候再去仔细探索它是怎么解析方法的。在validateServiceInterface方法之后才去通过Proxy去创建了一个动态代理类返回。

创建代理对象小结

1,必须是接口interface
2,接口中不能含有泛型参数,包括父接口也不能有
3,设置了validateEagerly的话会直接去解析加载每一个方法
复制代码

Android萌新必看:读一遍Retrofit源码,这里全是细节!

调用接口实现网络请求

这个步骤是最后一个步骤,因为调用了接口方法后,我们就能拿到返回值了,所以整个retrofit流程结束。要记住一点,Retrofit并不负责网络请求,它是网络请求的一个封装库,只是方便我们快速构建网络请求并拿到结果的一个中间件。

api.example()
    .enqueue(object : Callback<ResponseBody?> {
        override fun onResponse(call: Call<ResponseBody?>,response: Response<ResponseBody?>) {
            println("success: ${Thread.currentThread().name}")
        }

        override fun onFailure(call: Call<ResponseBody?>, t: Throwable) {
            t.printStackTrace()
        }
    })

如上,虽然调用的是Api#example方法,但是api对象却只是一个代理对象,实际走的是代理方法。而被代理的接口的每一个方法都会被Invocation#invoke方法所代理:

// Retrofit.java
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 {

                    // 若是Object中定义的方法,则直接调用
                    if (method.getDeclaringClass() == Object.class) {
                         return method.invoke(this, args);
                    }
                    args = args != null ? args : emptyArgs;
                    return platform.isDefaultMethod(method)
                        // 若是default方法,则通过lookup方式调用
                        ? platform.invokeDefaultMethod(method, service, proxy, args)
                        // 加载serviceMethod并调用
                        : loadServiceMethod(method).invoke(args);
                }
        });
}

因为Api对象是代理对象,所以方法的实现都是交给InvocationHandler#invoke去实现的,在invoke中,首先判断方法是否是Object类中定义的方法,是的话直接调用。然后是判断是否是default方法,是的话通过platform去调用。只有最后的普通接口方法,才会去进行代理,如下图:

Android萌新必看:读一遍Retrofit源码,这里全是细节!

所以,在调用接口方法的时候,会去排除一部分的方法,Object中的方法如toStringequals等,然后排除default方法,因为default方法有自己的方法体,不需要代理。最后剩下的方法才会去进行代理。

什么是default方法

前面有说到default方法,这里额外说一句,在java8中,接口类中不仅可以声明接口方法,也能声明带方法体的方法,但是需要使用default修饰。如下例:

public interface Example {
    // 普通接口方法
    void example();

    // default方法,可以有方法体
    default void exampleDefault() {
        System.out.println("deafult method")
    }
}

需要提一点的是,在Kotlin的接口类中,也是可以定义方法带方法体实现的,虽然表面看起来它和java的default方法是一样的,但实际上它却不是default方法。在Android Studio中,可以从 tools -> kotlin -> show kotlin bytecode查看kotlin中的字节码内容。也可以找到对应的class文件(位于:项目目录/app/build/tmp/kotlin=class/debug/包名/Api.class),然后使用javap -v Api.class查看。如下,我们定义一个Api接口,然后将其编译后的字节码文件显示出来是这样:

// 源代码Api.kt 
interface Api {
    fun example()

    fun test(){
        println("not default method")
    }
}

// Api.class字节码内容
public abstract interface com/learn/readretrofit/Api {

  // 接口方法example
  public abstract example()V
    LOCALVARIABLE this Lcom/learn/readretrofit/Api; L0 L1 0

  // 接口方法test,声明的时候带了方法体的
  public abstract test()V
    LOCALVARIABLE this Lcom/learn/readretrofit/Api; L0 L1 0

  // 静态内部类
  public final static INNERCLASS com/learn/readretrofit/Api$DefaultImpls com/learn/readretrofit/Api DefaultImpls
  // compiled from: Api.kt
}

public final class com/learn/readretrofit/Api$DefaultImpls {
  // 静态test方法,内容是我们写的方法体内容
  public static test(Lcom/learn/readretrofit/Api;)V
    ... 省略了具体实现,因为太长了
  // 
  public final static INNERCLASS com/learn/readretrofit/Api$DefaultImpls com/learn/readretrofit/Api DefaultImpls
  // compiled from: Api.kt
}

可以看到的是,实际上kotlin接口类中的带方法体的方法,是通过静态内部类中的静态方法实现的,而不是基本的default方法。那么也就是说,当我们在java中去实现kotlin中的接口方法的时候,无论是否有方法体,都需要重新实现该方法,如下例:

public class Demo implements Api{

    @Override
    public void example() {}

    @Override
    public void test() {
        // 在java中还必须实现这个方法
        // 但是可以通过这种方法去调用原来的方法体
        Api.DefaultImpls.test(this);
    }
}

扯远了扯远了,继续回到动态代理中,当排除了Object定义的方法和default方法后,对剩下的方法采用代理方式去调用,这个过程对应的是loadServiceMethod(method).invoke(args)

// Retrofit.java
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;
}

还记得在validateServiceInterface方法中,若是validateEagerly的值为true,则会去遍历所有的Method然后调用这个loadServiceMethod。当时并没有细讲,因为我们会在这里仔细说一下它到底做了什么。

简单从这个方法可以看到,首先从serviceMethodCache中去查找ServiceMethod,找不到的话再通过parseAnnotations生成一个对象并缓存起来,然后返回。其中serviceMethodCache是一个ConcurrentHashMap

Android萌新必看:读一遍Retrofit源码,这里全是细节!

获取ServiceMethod

所以,真正的网络请求是通过ServiceMethod#invoke实现的,这也是整个Retrofit核心的部分。所以下一步是找出ServiceMethod是如何生成的,可以继续追踪下去(这一步对应的上图的第2步):

static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    // 1,获取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);
}

这个过程又分为了三步:

Android萌新必看:读一遍Retrofit源码,这里全是细节!

创建RequestFactory

第一步是创建RequestFactory。其中RequestFactory是用来创建Reqeust的,它用来保存一个请求所需要的参数,如请求方式,请求参数,请求头等。而这些东西都是接口方法中通过注解进行配置的,所以这一步的主要目的就是解析这些参数。

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

简单看一个这个静态方法也是通过Builder模式去创建的实例,老规则直接跳到最后一步的build方法去查看它干了什么。

RequestFactory build() {
    // 1,转换方法注解
    for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
    }
    ...
    // 2,对方法的每个参数生成一个ParameterHandler,存在数组中
    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);
}

在上面的代码中,build方法中主要做了两件事,一是解析方法上的注解,另一件事是解析方法参数上的注解,然后每个参数生成一个ParameterHandler存放在数组上。而省略的那些代码是一些检验信息,比如没有body的一些请求方式不能含有Body,FormUrlEncoded必须有Field注解等。

Android萌新必看:读一遍Retrofit源码,这里全是细节!

解析方法上的注解

从上面截出来的build方法中可以看到,这一步是直接遍历方法上的所有注解,然后调用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);
    } 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;
    }
}

这个方法就是对注解进行解析,然后记录一些属性。比如请求方法GET或者POST啊,请求头Header等等。实在是太细碎了,就不一点一点的看了。我们记住这一步是解析方法注解的就行了,唯一要注意一点的是,@Header注解在解析后是存放在headers中的。

Android萌新必看:读一遍Retrofit源码,这里全是细节!

解析方法参数上的注解

其实上一步的代码很多,但是却是很多重复的代码。就是对每一种请求方式记录请求方法,请求路径,是否有body等等信息的一些记录,是比较无趣且没什么值得关注的。

而这一步是遍历解析方法中的每个参数,然后针对每个参数生成一个ParameterHander。因为这一步会涉及到的东西比较多,比如请求参数,body,是否是suspend方法,如何转化参数等等,所以还是比较有意思的。

注意一下,调用parseParameter方法去解析参数的时候,最后一个参数是p == lastParameter,也就是是否是最后一个参数。那么为什么要加最后这个参数来判断是否是最后一个参数呢?这是为了标识当前方法是否是一个suspend方法,在kotlin中,用suspend关键字去标识一个挂起方法。而在java中,挂起方法实际上是在方法参数列表中加入一个Continuation参数,并将返回值设为Object。

举个例子:

// 一个suspend方法
suspend fun test(page: Int): String

// 对应的java方法是这样的:
public final Object test(int page, Continuation<String> continuation)
复制代码

看到上面的例子就知道了,对于suspend方法,实际上是将返回值置为Object,然后在方法参数的最后一个位置上添加一个Continuation参数,其泛型类型就是实际的返回值。了解了这个基础知识后,再继续看parseParameter方法中做了什么。

//RequestFactory.java

RequestFactory build() {
    // 1,转换方法注解
    ...
    // 2,对方法的每个参数生成一个ParameterHandler,存在数组中
    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);
}

// 这个方法用来生成一个ParameterHandler
private @Nullable ParameterHandler<?> parseParameter(
    int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) {
    ParameterHandler<?> result = null;
    if (annotations != null) {
        // 参数上的注解不为空的时候,遍历注解
        // 注意一点,参数注解是可以有多个,但是只能存在一个REtrofit的注解
        for (Annotation annotation : annotations) {
            ParameterHandler<?> annotationAction =
                parseParameterAnnotation(p, parameterType, annotations, annotation);

            if (annotationAction == null) {
                continue;
            }

            if (result != null) {
                throw parameterError(...);
            }

            result = annotationAction;
        }
    }

    // 若是没能创建出对应的ParameterHandler,则去考虑是否是suspend方法的Continuation参数
    // 不是的话直接抛出异常
    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;
}

这一步仍然是调用了其他方法去生成ParameterHandler,我们知道一个参数上是可以添加多个注解的,所以这里就直接遍历这个注解,然后通过parseParameterAnnotation去生成ParameterHandler。并且,要求在多次遍历中,最多只有一个注解能够生成实例。也就是说,在方法的一个参数上,只能有一个Retrofit中的注解,因为只有Retrofit中的注解才会生成ParameterHandler。

并且,若是没能生成的ParameterHandler的话,这个参数只能是最后一个参数并且是Continuation类型,也就是说必须是suspend方法生成的那个Continuation

所以这里我们就知道了:

1,接口方法上的参数,每个参数只能有一个Retrofit注解
2,每个参数必须有一个Retrofit注解
3,Continuation是suspend方法自动生成的字段,我们不用管

所以,每个参数有且只能有一个Retrofit注解(@Path/@Query这种)
复制代码

为什么我们说只有Retrofit的注解才会生成ParameterHandler呢,这就需要接着去看下一个调用方法,方法名是parseParameterAnnotation,前面遍历参数的每个注解然后调用的就是这个方法。但是这个方法有大概450行,因此这里删去了很多细节。

    private ParameterHandler<?> parseParameterAnnotation(
        int p, Type type, Annotation[] annotations, Annotation annotation) {
      if (annotation instanceof Url) {
      } else if (annotation instanceof Path) {
        ...
        Path path = (Path) annotation;
        String name = path.value();
        ...
        Converter<?, String> converter = retrofit.stringConverter(type, annotations);
        return new ParameterHandler.Path<>(method, p, name, converter, path.encoded());

      } else if (annotation instanceof Query) {
      } else if (annotation instanceof QueryName) {
      } else if (annotation instanceof QueryMap) {
      } else if (annotation instanceof Header) {
      } else if (annotation instanceof HeaderMap) {
      } else if (annotation instanceof Field) {
      } else if (annotation instanceof FieldMap) {
      } else if (annotation instanceof Part) {
      } else if (annotation instanceof PartMap) {
      } else if (annotation instanceof Body) {
        Converter<?, RequestBody> converter;
        try {
          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;
        return new ParameterHandler.Body<>(method, p, converter);
      } else if (annotation instanceof Tag) {
      }

      return null;
    }

在这个方法中,使用了一个很大的if-else语句将每一个用于参数的Retrofit注解给处理了一遍,所以知道了吧,每个Retrofit的注解方法都一定能生成一个对应的ParameterHandler,除非抛出异常。

并且大部分的if分支中的代码都是差不多的,分为两个步骤:一是获取参数对应的Converter,二是new一个对应的ParameterHandler的子类对象返回。这里只保留了两个if分支,一个是Path,一个是body。为什么只保留他们俩呢,因为他们比较特殊。首先是Path,在获取它的Converter的时候,走的是retrofit.stringConverter,这是其他注解也会用的,所以只用分析它一个即可。而Body注解用的是retrofit.requestBodyConverter去获取的Converter,所以这里也需要对它分析一下。

先看一下@Path对应的分支块,首先通过retrofit.stringConverter方法去获取Converter:

public <T> Converter<T, String> stringConverter(Type type, Annotation[] annotations) {
    Objects.requireNonNull(type, "type == null");
    Objects.requireNonNull(annotations, "annotations == null");

    // 首先遍历converterFactories,查看是否有能处理该类型的Factory
    for (int i = 0, count = converterFactories.size(); i < count; i++) {
      Converter<?, String> converter =
          converterFactories.get(i).stringConverter(type, annotations, this);
      if (converter != null) {
        return (Converter<T, String>) converter;
      }
    }

    // 都没有的话使用默认的Converter
    return (Converter<T, String>) BuiltInConverters.ToStringConverter.INSTANCE;
  }

首先就是去遍历converterFactories,这个集合在构造retrofit实例的时候讲到过的。若是没有找到能够处理该参数的Factory,则使用默认的BuiltInConverters.ToStringConverter,这个Converter做的非常简单,仅仅是调用toString方法就完了。

因为我们若是没添加额外的ConverterFactory的话,默认是只有BuiltInConvertersOptionalConverterFactory。而这两个Factory都没有去重写stringConverter,所以默认会用到这个ToStringConverter

static final class ToStringConverter implements Converter<Object, String> {
    static final ToStringConverter INSTANCE = new ToStringConverter();

    @Override
    public String convert(Object value) {
      return value.toString();
    }
}

从这里我们可以知道,我们的Api.kt中方法的每个参数都会配一个Converter,用于将参数转换成我们需要的类型,这里是将其转换成String类型。因此,拥有Path/Query/QueryName/QueryMap/Header/HeaderMap/Field/FieldMap这些注解的参数,最终是会变成String的,若是我们不想简单的使用toString来转换,则可以在创建retrofit的时候去添加一个我们的ConverterFactory,并重写stringConverter方法去说明你要处理哪种类型。

而对于Body注解的参数,使用的并不是stringConverter,而是用了retrofit.requestBodyConverter

public <T> Converter<T, RequestBody> requestBodyConverter(
      Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations) {
    return nextRequestBodyConverter(null, type, parameterAnnotations, methodAnnotations);
}

public <T> Converter<T, RequestBody> nextRequestBodyConverter(
      @Nullable Converter.Factory skipPast,
      Type type,
      Annotation[] parameterAnnotations,
      Annotation[] methodAnnotations) {

    int start = converterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = converterFactories.size(); i < count; i++) {
        Converter.Factory factory = converterFactories.get(i);
        Converter<?, RequestBody> converter =
           factory.requestBodyConverter(type, parameterAnnotations, methodAnnotations, this);
        if (converter != null) {
          return (Converter<T, RequestBody>) converter;
        }
    }
    ...
    throw new IllegalArgumentException(builder.toString());
}

也是一样的,最终都是遍历Converterfactories去查找是否有能处理该类型的Factory。注意,其实所有的类型转换都是通过ConverterFactory去实现的。但是这个方法就比stringConverter激进一点了,因为它找不到的话直接给抛出异常而不是给一个默认的Converter

但是,和stringConverter不同的一点是。默认的BuiltInConverters重写了requestBodyConverter去拦截了某种类型,所以默认是有能够处理的Converter的,这也是为什么若是都找不到的话直接抛出异常而不是再给一个默认的转换器。

final class BuiltInConverters extends Converter.Factory {
    ...
    @Override
    public @Nullable Converter<?, RequestBody> requestBodyConverter(
      Type type,
      Annotation[] parameterAnnotations,
      Annotation[] methodAnnotations,
      Retrofit retrofit) {

        if (RequestBody.class.isAssignableFrom(Utils.getRawType(type))) {
            return RequestBodyConverter.INSTANCE;
        }
        return null;
    }
    ...
}

从上面的代码可以看出,它的判断条件是该参数的类型是RequestBody的时候才会返回一个Converter实例。所以,在没有添加其他ConverterFactory的时候,@Body注释的参数一定要确保是RequestBody类型。

看到没,每个参数都会去converterFactories中去遍历查找是否有能够处理这种参数类型的Converter,一旦找到就会直接返回。因此Factory在集合中的顺序是很重要的,若是你写了一个Factory,能够处理任何类型的参数的话,一定要确保最后添加进集合,不然其他的Factory就会被忽略而无法起到作用的。

最后就是生成对应的ParameterHandler了,而ParameterHandler是个抽象方法,他有一系列的子类,对应每一个注解。比如Path注解对应的是ParameterHandler.Path<T>类。然后就是直接new了一个RequestFactory并返回了,到这里,我们对每一个参数都有了一个对应的ParameterHandler了,并且每个Handler里面都有一个Converter用于将参数转换成String类型。

到这里创建RequestFactory的流程就完了,在RequestFactory.Builder.build方法中的最后一行,直接new出了一个RequestFactory,流程到此结束。

Android萌新必看:读一遍Retrofit源码,这里全是细节!

这一节比较长,方法调用的也比较多,还记得我们现在处于哪个流程吗?可以看有右侧目录,我们当前处于创建ReqeustFactory章节,再贴一下图:

Android萌新必看:读一遍Retrofit源码,这里全是细节!

我们第一步已经看完了,也知道了怎么生成的一个RequestFactory以及它里面有什么内容,那么接下来继续看第二步对返回值进行处理和第三步获取到ServiceMethod吧。再回到创建ServiceMethod的流程中:

static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    // 1,获取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);
}

其中,第一步我们已经分析完了,第二步是判断返回值类型是否有效,这里不深入了,主要就是判断返回值类型不能含有?通配符。比如User<?>这种类型是不被允许的。

创建ServiceMethod实例

Android萌新必看:读一遍Retrofit源码,这里全是细节!

那么就是最后一步,真正的创建ServiceMethod了。上图是创建ServiceMethod的四个步骤。先看第一个步骤,根据返回值获取callAdapter。注意因为suspend方法的返回值一直是Object,所以首先是重整返回值:

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    // 判断是否是suspend方法
    boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
    // 判断返回值是否想要Response包裹,Response<T>这种类型
    boolean continuationWantsResponse = false;
    boolean continuationBodyNullable = false;// 这个参数在当前版本没有处理,一直是false

    Annotation[] annotations = method.getAnnotations();
    Type adapterType;
    if (isKotlinSuspendFunction) {
      Type[] parameterTypes = method.getGenericParameterTypes();
      // 对于suspend方法,需要通过最后一个参数Continuation来获取他的实际返回值
      Type responseType = Utils.getParameterLowerBound(
          0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
      // 返回值是Response<T>类型
      if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
        // 获取形如Response<T>中的T的类型
        responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
        continuationWantsResponse = true;
      } else {
      }
      // 使用自定义的类型,去重新设计返回值类型
      adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
      annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
    } else {
      adapterType = method.getGenericReturnType();
    }
    ...
  }

上面是parseAnnotations方法的前面一部分,也就是该方法的第一步,获取返回值。首先,根据RequestFactory判断是否是suspend方法,前面说过,这个过程是通过看最后一个参数是不是Continuation来进行判断的。然后从Continuation中获取实际的返回值,若是返回值被Response包裹的话,还要将Reponse层去掉。最后拿到真正的类型后,通过自定义类型去重新将返回值设计为Call<xxx>类型。当然,若不是suspend的方法的话,直接取的就是返回值,没有其他任何操作。

可能说着有点绕,通过下面的示例代码,看一下如何将suspend方法的返回值重组的:

// 例如这是原来的接口方法
suspend fun example():Response<ResponseBody>

//这个方法在java眼中是这样的:
public final Object example(Continuation<Response<ResponseBody>> continuation)

// 通过这个方法拿到的是这种类型 Response<ResponseBody>
Type responseType =
        Utils.getParameterLowerBound(
            0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);

// 再去一层,这时候responseType的类型为ResponseBody
// 当然,若是接口方法没用Response包裹的话,则不需要再对此去掉一层了
responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);

// 最后一步,使用自定义的类型,将返回值重新包裹一下
// 这时候adapterType的结果是这样的: Call<ResponseBody>
adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);

所以,第一步获取返回值,对于普通方法,可以直接拿到返回值,对于suspend方法,则需要进行一系列的操作才能拿到返回值,并且还将返回值给包装成了Call<T>的类型。

接下来继续看后面的代码:

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {

    ...
    // 上一步的解析获取到了返回值类型
    adapterType = method.getGenericReturnType();

    // 1,获取CallAdapter
    CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
    // 拿到callAdapter适配后要返回的类型,例如Call<ReponseBody>,则返回的应该是ResponseBody
    Type responseType = callAdapter.responseType();
    // 返回值不能是okhttp3.Response
    if (responseType == okhttp3.Response.class) {
      throw methodError(...);
    }
    // 返回值若是retrofit.Response的话,必须指定泛型类型
    if (responseType == Response.class) {
      throw methodError(...);
    }
    // 若是网络请求方法是Head方法,则返回值必须为空
    // 从这里的注释可以看出,这里只判断了java的返回值空,未判断kotlin的空返回值
    // TODO support Unit for Kotlin?
    if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
      throw methodError(method, "HEAD method must use Void as response type.");
    }

    // 2,根据适配后的类型获取Converter
    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);
    ...
}

所以拿到了返回值之后,下一步还是根据返回值去获取相应的CallAdapter。这个调用链比较长,但是却比较简单,最终的结果就是从创建Retrofti示例的时候创建的那个CallAdapterFactories的集合中去遍历查找。

private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
      Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {
    ...
    return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
    ...
}

public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
}

public CallAdapter<?, ?> nextCallAdapter(
      @Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) {

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

获取CallAdapter的方法是上面这样的,经过一系列的调用,最后还是去遍历callAdapterFactories去用他们的get方法去获取CallAdapter实例,未找到的话则会抛出异常。若是还记得话,刚开始在创建Retrofit的时候,我们加入了两个callAdapterFactory,分别是DefaultCallAdapterFactoryCompletableFutureCallAdapter。我们看一下他们的get方法是怎么去实现的。

final class DefaultCallAdapterFactory extends CallAdapter.Factory {
  ...

  @Override
  public @Nullable CallAdapter<?, ?> get(
      Type returnType, Annotation[] annotations, Retrofit retrofit) {
    // 只处理Call<T>类型的返回值
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    // Call必须带泛型,不能是Call<?>这种类型
    if (!(returnType instanceof ParameterizedType)) {
      throw new IllegalArgumentException(
          "Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
    }
    final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);
    ...
    return new CallAdapter<Object, Call<?>>() {...};
  }
}

final class CompletableFutureCallAdapterFactory extends CallAdapter.Factory {
  static final CallAdapter.Factory INSTANCE = new CompletableFutureCallAdapterFactory();

  @Override
  public @Nullable CallAdapter<?, ?> get(
      Type returnType, Annotation[] annotations, Retrofit retrofit) {

    // 只处理CompletableFuture<T>类型的返回值
    if (getRawType(returnType) != CompletableFuture.class) {
      return null;
    }
    // CompletableFuture必须带泛型参数,不能是Completable<?>类型的
    if (!(returnType instanceof ParameterizedType)) {
        throw new IllegalStateException(...);
    }
    Type innerType = getParameterUpperBound(0, (ParameterizedType) returnType);

    // 非Response<T>类型
    if (getRawType(innerType) != Response.class) {
        return new BodyCallAdapter<>(innerType);
    }

    // Response<T>类型,重新将T类型拿出,赋值给responseType
    Type responseType = getParameterUpperBound(0, (ParameterizedType) innerType);
    return new ResponseCallAdapter<>(responseType);
  }
    ...
}

从这两个CallAdapterFactory上来看,他们都是对返回值的最外层类型进行判读那的。其中DefaultCallAdapterFactory 只能转换返回值类型为Call<T>的方法。注意,对于suspend方法,最终是被封装成Call<T>返回值类型的,因此是可以被这个Factory产生的CallAdapter进行转换的。

CompletableFutureCallAdapterFactory也是一样,只能处理CompetableFuture<>CompetableFuture<Response<T>>类型的返回值,并给出了不同的CallAdapter。

因此,根据这两种callAdapter我们可以总结出以下几种接口方法的写法,这些是我们没有添加额外的callAdapter的前提下可以用的:

interface Api {
    // 其中xxx代表具体类型,比如ResponseBody
    // 该类型与ConverterFactory有关 
    fun test():Call<xxx>
    fun test1():CompletableFuture<Response<xxx>>
    fun test2():CompletableFuture<xxx>
    suspend fun test3():xxx
    suspend fun test4():Response<xxx>
}

Android萌新必看:读一遍Retrofit源码,这里全是细节!

接下来就是Converter的获取,同样的,追踪下来发现也是去遍历converterFactories的Factory,通过其responseBodyConverter方法去获取Converter。

默认情况下也是有两个ConverterFactory被添加进去的,分别是BuiltInConvertersOptionalConverterFactory。先看一下BuiltInConverters:

final class BuiltInConverters extends Converter.Factory {
  private boolean checkForKotlinUnit = true;

  @Override
  public @Nullable Converter<ResponseBody, ?> responseBodyConverter(
      Type type, Annotation[] annotations, Retrofit retrofit) {

    if (type == ResponseBody.class) {
      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;
  }
  ...
}

也是比较简单,主要分为两种Converter,一种是类型为ResponseBody,一种是空类型(两种,java的Void和kotlin的Unit)。注意一点,对于ResponseBody的类型,还分为了两种Converter。若是方法被@Streaming修饰的话,返回的StreamingResponseBodyConverter,否则返回的是BufferingResponseBodyConverter

通常,我们在下载的时候会使用@Streaming修饰,因为这样不会将接口返回的数据全部加载到内存中,而是和服务器建立了一个连接,我们可以通过ResponseBody获取到流,然后对其进行读写。若是不用@Streaming修饰的话,正常的一次请求会将服务器返回的内容缓存到内存中,然后再返回。

上面是BuiltInConverters所做的事,那么,OptionalConverterFactory又是处理什么类型的呢?

final class OptionalConverterFactory extends Converter.Factory {
    static final Converter.Factory INSTANCE = new OptionalConverterFactory();

    @Override
    public @Nullable Converter<ResponseBody, ?> responseBodyConverter(
      Type type, Annotation[] annotations, Retrofit retrofit) {
        if (getRawType(type) != Optional.class) {
            return null;
        }

        Type innerType = getParameterUpperBound(0, (ParameterizedType) type);
        Converter<ResponseBody, Object> delegate =
            retrofit.responseBodyConverter(innerType, annotations);
        return new OptionalConverter<>(delegate);
    }
    ...
}

可以看到,它处理的是Optional类型的返回值,但他却只是一个包装,实际的Converter仍然是去掉外层Optional后,再去遍历查询得到的Converter。前面说了BuiltInConverters后,总结出其中5中接口方法的写法。这里加上了OptionalConverterFactory后,就可以为每种写法的返回值外面都套上一层或者多层Optional,所以至少是10种写法。

但就此看来,这种写法对于我们而言用处确实是比较小的,因为咱们直接用了kotlin啊。

Android萌新必看:读一遍Retrofit源码,这里全是细节!

这时候,返回值和CallAdapter和Converter都已经获取完毕了,接下来就是最后一步的去生成实际的ServiceMethod了。

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
    boolean continuationWantsResponse = false;
    boolean continuationBodyNullable = false;

    // 1,获取返回值
      adapterType = method.getGenericReturnType();

    // 2,获取CallAdapter
    CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);

    // 3,获取Converter
    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);

    okhttp3.Call.Factory callFactory = retrofit.callFactory;

    // 4,生成实际的ServiceMethod
    if (!isKotlinSuspendFunction) {
      // 非suspend方法对应的ServiceMethod
      return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    } else if (continuationWantsResponse) {
      // Response<T> 类型的suspend方法的ServiceMethod
      return (HttpServiceMethod<ResponseT, ReturnT>)
          new SuspendForResponse<>(
              requestFactory,
              callFactory,
              responseConverter,
              (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
    } else {
      // 其他类型suspend方法对应的ServiceMethod
      return (HttpServiceMethod<ResponseT, ReturnT>)
          new SuspendForBody<>(
              requestFactory,
              callFactory,
              responseConverter,
              (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
              continuationBodyNullable);
    }
  }

所以,实际上我们获取到的ServiceMethod一共有三种类型。分别是正常的CallAdapted,和想要Response层的SuspendForResponse,以及正常的SuspendForBody。这三种类型都是HttpServiceMethod的子类,而HttpServiceMethod又是ServiceMethod的子类。至于它们到底做了什么,则需要看下一步了,也就是最后一步,通过invoke拿到代理方法的返回值并返回。

Android萌新必看:读一遍Retrofit源码,这里全是细节!

调用ServiceMethod.invoke实现代理方法的调用

最后一步是调用ServiceMethod#invoke拿到返回值。而ServiceMethod是一个抽象类,其中invoke是抽象方法,具体的实现是在其子类HttpServiceMethod中实现的。而我们拿到的这三种ServiceMethod,都是继承自HttpServiceMethod的,所以我们先看一下HttpServiceMethod。

abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {
    ...
    private final RequestFactory requestFactory;
    private final okhttp3.Call.Factory callFactory;
    private final Converter<ResponseBody, ResponseT> responseConverter;
    ...

    @Override
    final @Nullable ReturnT invoke(Object[] args) {
        Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
        return adapt(call, args);
    }

    protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);
    ...
}

他实际创建了一个OkHttpCall,然后又搞了一个抽象方法adapter出来。所以我们可以看到,实际的转换流程就是发生在这个抽象方法中。

这里注意,这个OkHttpCall不是okhttp的,它仍是属于retrofit的,不要被他的名字骗到了。并且他实现了retrofit.Call,这个Call中的方法和okhttp.Call接口的方法是一致的。也就是说,我们可以像使用okhttp.Call那样去使用这个retrofit.Call

我们知道,Retrofit不是一个网络请求框架,而是一个网络请求框架的封装框架,他是用来简化网络请求的步骤的,而不是负责去进行网络请求的。所以这里它尽量减少对OkHttp的依赖,使用了代理模式,将OkHttp相关的东西都放在内部,对外开放的是属于Retrofit的方法。当然为了考虑到使用的习惯性,这些方法的方法名和参数都是与OkHttp是一致的。

通过上面的分析我们也知道,invoke方法最终是通过adapt方法返回的。也就是说,实际的流程应该在这里。我们现在是有三种ServiceMethod的,就分别看一下他们各是怎么去实现的:

static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {
    private final CallAdapter<ResponseT, ReturnT> callAdapter;
    ...

    @Override
    protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
        return callAdapter.adapt(call);
    }
}

普通的方法对应的HttpServiceMethod啥都没做,返回的就是经过callAdapter转换后的结果。

Android萌新必看:读一遍Retrofit源码,这里全是细节!

但suspend方法就不一样了:

static final class SuspendForResponse<ResponseT> extends HttpServiceMethod<ResponseT, Object> {
    private final CallAdapter<ResponseT, Call<ResponseT>> callAdapter;
    ...

    @Override
    protected Object adapt(Call<ResponseT> call, Object[] args) {
      call = callAdapter.adapt(call);
      Continuation<Response<ResponseT>> continuation =
          (Continuation<Response<ResponseT>>) args[args.length - 1];
      try {
        return KotlinExtensions.awaitResponse(call, continuation);
      } catch (Exception e) {
        return KotlinExtensions.suspendAndThrow(e, continuation);
      }
    }
}

static final class SuspendForBody<ResponseT> extends HttpServiceMethod<ResponseT, Object> {
    private final CallAdapter<ResponseT, Call<ResponseT>> callAdapter;
    private final boolean isNullable;
    ...

    @Override
    protected Object adapt(Call<ResponseT> call, Object[] args) {
      call = callAdapter.adapt(call);
      Continuation<ResponseT> continuation = (Continuation<ResponseT>) args[args.length - 1];
      try {
        return isNullable
            ? KotlinExtensions.awaitNullable(call, continuation)
            : KotlinExtensions.await(call, continuation);
      } catch (Exception e) {
        return KotlinExtensions.suspendAndThrow(e, continuation);
      }
    }
}

仔细看一下就能发现,这俩几乎是一样的。首先通过adapter去将OkHttpCall进行转换,然后转换完之后并没有去返回,而是又去调用了Call的拓展方法,最后才返回。那么这个拓展方法又做了什么呢?从前面的分析我们知道,我们在写suspend方法的时候,返回值要写成具体的类型或者Response<T>这种形式,而这种形式显然是已经完成了网络请求后的结果。所以猜测,这个拓展方法中做的事肯定是直接进行网络请求了。

它们两个,调用的一共是四种拓展方法:

KotlinExtensions.awaitResponse
KotlinExtensions.awaitNullable
KotlinExtensions.await

KotlinExtensions.suspendAndThrow

一个一个的去看一下吧,首先是KotlinExtensions.awaitResponse,这是对应的suspend方法并且返回值是Response<>这种形式的接口方法:

suspend fun <T> Call<T>.awaitResponse(): Response<T> {
  return suspendCancellableCoroutine { continuation ->
    continuation.invokeOnCancellation {
      cancel()
    }
    enqueue(object : Callback<T> {
      override fun onResponse(call: Call<T>, response: Response<T>) {
        continuation.resume(response)
      }

      override fun onFailure(call: Call<T>, t: Throwable) {
        continuation.resumeWithException(t)
      }
    })
  }
}

这是Call的一个拓展方法,执行过程是创建一个协程体,然后在其中通过Call.enqueue去执行网络请求,然后在回调中通过Continuation去通知原来的suspend方法返回。

注意,在没有执行Continuation的resume方法前,协程是会被挂起,直到执行了resume方法才会恢复继续执行。所以这时候我们就能直接拿到了返回值了。

下一个是KotlinExtensions.awaitNullable,这种方法要求的返回值可以返回一个可空的结果。实际上这个方法是不会调用到的,因为continuationBodyNullable一直是false,目前2.9.0版本并未对这个值进行修改:

// 这是awaitNullable方法,注意方法名和jave层调用是有些差入的。
suspend fun <T : Any> Call<T?>.await(): T? {
  return suspendCancellableCoroutine { continuation ->
    continuation.invokeOnCancellation {
      cancel()
    }
    enqueue(object : Callback<T?> {
      override fun onResponse(call: Call<T?>, response: Response<T?>) {
        // 对返回值进行了去壳,只要response.body
        if (response.isSuccessful) {
          continuation.resume(response.body())
        } else {
          continuation.resumeWithException(HttpException(response))
        }
      }

      override fun onFailure(call: Call<T?>, t: Throwable) {
        continuation.resumeWithException(t)
      }
    })
  }
}

接下来看KotlinExtensions.await方法,这才是实际调用的方法。它对应的suspend方法的正常返回,没有Response包裹的那种请求方式:

suspend fun <T : Any> Call<T>.await(): T {
  return suspendCancellableCoroutine { continuation ->
    continuation.invokeOnCancellation {
      cancel()
    }
    enqueue(object : Callback<T> {
      override fun onResponse(call: Call<T>, response: Response<T>) {
        if (response.isSuccessful) {
          val body = response.body()
          // 要求body不能为空
          if (body == null) {
            ...
            continuation.resumeWithException(e)
          } else {
            continuation.resume(body)
          }
        } else {
          continuation.resumeWithException(HttpException(response))
        }
      }

      override fun onFailure(call: Call<T>, t: Throwable) {
        continuation.resumeWithException(t)
      }
    })
  }
}

简单看一下这三个方法就是一样的,唯一的区别就是对返回值是否需要去壳和判空。通过这几个方法的分析,我们知道使用suspend声明的接口方法在调用的时候是会直接去执行网络请求然后给出结果的。并且,由于使用的是enqueue方法去执行的网络请求,网络请求会在子线程中执行的。所以我们在调用这个方法的时候,可以直接在主线程中使用,而不用额外切换一层的,例如下面:

// 可以直接这样写
lifecycleScope.launch {
    val response = api.suspendCall()
    textView.text = response.string()
}

// 这种是没必要的
lifecycleScope.launch {
    val response = withContext(Dispatchers.IO) { api.suspendCall() }
    textView.text = response.string()
}

实际上,suspend关键字的意思除了挂起外,还有一层意思就是指明这个函数有可能是耗时的,所以声明成了挂起函数,需要在协程代码块中执行。所以,当声明一个suspend方法的时候,该方法应该自行处理耗时操作所处的线程,而不应该交由给外部去进行切换。也就是说,调用者只需要处理自己所在的线程环境,而不应该去关心要调用的suspend方法中执行的细节。

Android萌新必看:读一遍Retrofit源码,这里全是细节!

前面的分析,我们也知道了,Retrofit一共是两种adapter的,一个是callAdapter,用于将请求转换的,这个我们已经讲过了。另一个是converterAdapter,是用来处理返回值的,那么它又是什么时候去转换的呢?

我们知道实际上在调用SeviceMethod#invoke的时候,是去new了一个OkHttpCall,然后再去交给callAdapter去转换的,所以我们的实际的网络请求对象就是这个OkHttpCall:

public void enqueue(final Callback<T> callback) {
   ...
   // createRawCall会使用callFactory去创建一个okhttp3.Call
   call = rawCall = createRawCall();
   ...
   // 调用OkHttp3.Call#enqueu
   call.enqueue(
       new okhttp3.Callback() {
           @Override
           public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
              Response<T> response;
               try {
                  // 通过ConverterAdapter转换结果
                  response = parseResponse(rawResponse);
               } catch (Throwable e) {
                  throwIfFatal(e);
                  callFailure(e);
                  return;
               }
           ...
           }
         ...
        });
}

上面是截取了请求的一部分,发现在获取了实际的网络请求结果之后,通过parseResponse方法将Resposne结果进行转换,然后才通过callback进行回调。所以ConveterAdapter肯定是在这个方法中去实现的,所以仔细看一下这个方法:

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

    // 重新创建了一个Response,但是这个reponse没有内容
    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);
    }

    ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
    try {
      // 在这里通过converterAdapter处理返回值
      T body = responseConverter.convert(catchingBody);
      return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
      catchingBody.throwIfCaught();
      throw e;
    }
  }

可以看到,在这个方法中,重新创建了一个一样Response,但是却是没有body内容的。这个response只包含了请求结果的状态长度等信息,还记得回调方法的两个参数吗,其中有一个参数就是它。

接下来是判断返回值,小于200和大于等于300的都会返回一个Response.error。204和205返回的是success,但是没有body内容。剩下的才会有实际内容,注意这里用了一个ExceptionCatchingResponseBody去将原始的rawBody包装了一下,这里用的实际是代理模式,其中Response.source方法被代理了,使用了Okio去代理,其他方法都没做任何处理,直接调用的rawBody的方法。最后才是使用ConverterAdapter去转换结果。

Android萌新必看:读一遍Retrofit源码,这里全是细节!

总结

到这里其实我们已经将Retrofit分析完了,过程比较多比较细。通过这次通读,我们也知道了Retrofit实际上是并没有网络请求的,实际的请求过程是交给了OkHttp去实现的。

所以Retrofit主要功能是简化网络请求的,它通过接口方法去声明一个网络请求所需要的各种参数,然后通过动态代理去返回网络请求的实体。并且提供了CallAdapter将请求实体Call转换成我们需要的形式,以及提供了ConverterAdapter将请求后的结果转换成我们需要的形式。

对于我们使用而言,这两类Adapter也是最重要的点了,我们可以通过它们去定义各种适合的形式。

需要项目实战视频和面试真题解析可以去我的B站看
Android项目实战合集:https://space.bilibili.com/549975384

上一篇:Java进阶(二十)解疑答惑之何时字符串才算真正为空?


下一篇:如何处理PHP中不推荐使用的功能