Retrofit之CallAdapter解析

Retrofit之CallAdapter解析

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情

前言

Retrofit源码系列文章:

在Retrofit源码里面,网络请求适配器CallAdapter是一个出现得比较频繁的接口,在调用接口方法发起网络请求的时候,必然要调用CallAdapter的adapt方法。那CallAdapter实际上到底是什么,Retrofit为什么要设计这个接口,本文就来进一步扒一扒CallAdapter。

PS:本文基于Retrofit版本2.8.0,在阅读本文之前建议先了解Retrofit的源码,可以参考挖一挖Retrofit源码(一)挖一挖Retrofit源码(二)

正文

用过Retrofit的朋友应该都熟悉下面的网络请求接口的常规写法:

interface TestApi {
    @GET("/.../...")
    fun getDataA(): Call<DataBean>
}
复制代码

接口方法getDataA的返回类型是Call,那思考一下想让这个方法直接返回DataBean可以吗?在初始化Retrofit时没有添加任何CallAdapterFactory和不考虑挂起接口方法的情况下,答案是不可以的,此时getDataA的返回类型必须是Retrofit的Call而不能是其他类型,至于为什么一定要返回Call呢?继续看下去。

CallAdapter是什么

在分析之前,首先来搞清楚CallAdapter是什么,顾名思义CallAdapter就是Call(retrofit2.Call)的适配器,用来适配不同返回类型的网络请求接口方法使其能正常调用OkHttpCall来发起网络请求,并返回该方法指定的返回类型的数据。

先来看下CallAdapter的源码:

public interface CallAdapter<R, T> {
  Type responseType();

  T adapt(Call<R> call);

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

    protected static Type getParameterUpperBound(int index, ParameterizedType type) {
      return Utils.getParameterUpperBound(index, type);
    }

    protected static Class<?> getRawType(Type type) {
      return Utils.getRawType(type);
    }
  }
}
复制代码

其中CallAdapter<R, T>中的泛型R是API方法的原始返回类型(报文中的数据结构),泛型T是适配后的返回类型(期望拿到的数据结构)。接口里只有两个方法:responseType()和adapt(),根据源码给出的注释可以知道:

  • responseType()是用来获取接口方法的响应类型的,例如当报文返回的类型带泛型时,responseType就是泛型T,当报文返回的类型不带泛型时,responseType直接就是返回的那个类
  • adapt()将Call对象转换成一个泛型T对象

在接口的内部还定义了一个抽象类Factory,其中的get方法会根据接口方法的returnType来返回对应的CallAdapter,getParameterUpperBound方法的作用是返回第index个参数的最上层的类,getRawType方法的作用是返回参数的原始类型。

当然,单看这段源码很难知道CallAdapter具体是干嘛的,responseType()和adapt()这两个方法是在子类中实现的,目前Retrofit提供了以下几种CallAdapterFactory:DefaultCallAdapterFactory、RxJavaCallAdapterFactory、CompletableFutureCallAdapterFactory、GuavaCallAdapterFactory、ScalaCallAdapterFactory,没有添加CallAdapterFactory的话是默认使用DefaultCallAdapterFactory,接下来就看看CallAdapter的两个方法在DefaultCallAdapterFactory里具体是怎么实现的。

DefaultCallAdapterFactory

final class DefaultCallAdapterFactory extends Factory {
    @Nullable
    private final Executor callbackExecutor;

    DefaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
        this.callbackExecutor = callbackExecutor;
    }

    @Nullable
    public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
        //接口方法返回类型必须是Call<T>
        if (getRawType(returnType) != Call.class) {
            return null;
        } else if (!(returnType instanceof ParameterizedType)) {
            throw new IllegalArgumentException("Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
        } else {
            //获取响应类型
            final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType)returnType);
            final Executor executor = Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class) ? null : this.callbackExecutor;
            return new CallAdapter<Object, Call<?>>() { //Call<?>为Retrofit的Call
                public Type responseType() {
                    return responseType;
                }

                public Call<Object> adapt(Call<Object> call) {
                    return (Call)(executor == null ? call : new DefaultCallAdapterFactory.ExecutorCallbackCall(executor, call));
                }
            };
        }
    }
    
    static final class ExecutorCallbackCall<T> implements Call<T> {
        final Executor callbackExecutor;
        final Call<T> delegate;

        ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
            this.callbackExecutor = callbackExecutor;
            this.delegate = delegate;
        }

        public void enqueue(final Callback<T> callback) {
            Objects.requireNonNull(callback, "callback == null");
            this.delegate.enqueue(new Callback<T>() {
                ......
            });
        }
    }
}
复制代码

代码很简单,DefaultCallAdapterFactory继承了CallAdapter.Factory这个类,在get方法里面主要做了以下的事:

  • 对接口方法的返回类型returnType进行了检测,要求returnType的原始类型必须是Call,如果不是则返回null
  • 获取方法的响应类型,即Call中的T,然后由CallAdapter的responseType方法返回
  • CallAdapter的adapt方法最终返回了ExecutorCallbackCall对象

分析到这里想必大家也清楚在前面提到的前提条件下为什么接口方法必须要返回Call了,至于ExecutorCallbackCall实际上是OkHttpCall的一个代理类,在调用接口方法的时候进行异步请求时网络请求和回调处理的工作实际上是由OkHttpCall的enqueue方法去完成的,回顾一下Retrofit的create方法:

public <T> T create(final Class<T> service) {
    validateServiceInterface(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 {
            // If the method is a method from Object then defer to normal invocation.
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          }
        });
  }
复制代码

Retrofit的核心在于动态代理,调用接口方法时动态代理对象会将方法转发到InvocationHandler的invoke方法中去处理,创建出这个方法的ServiceMethod对象然后调用HttpServiceMethod的invoke去发起请求,而实际上调用的正是CallAdapter的adapt方法,也就是调用链路实际上为loadServiceMethod.invoke -> ServiceMethod.invoke -> HttpServiceMethod.invoke->CallAdapter.adapt->ExecutorCallbackCall.enqueue->OkHttpCall.enqueue

那么问题来了,既然实际上干活的是OkHttpCall,OkHttpCall实现了Call接口,那在HttpServiceMethod的invoke方法里直接返回OkHttpCall不就好了,这样调用getDataA一样能拿到正确的数据,返回的Call就是OkHttpCall了,那还要这个CallAdapter干嘛?别急,Retrofit设计这个接口必然是有它的道理,继续往下看。

CallAdapter到底有什么意义

假如说在HttpServiceMethod的invoke方法里直接返回OkHttpCall的话,就可以直接操控这个OkHttpCall对象去完成网络请求的工作,然而这样一来,接口方法的返回类型就只能是Call了,那要是我不想返回Call呢?我想要返回自定义的Data类或者RxJava的Observable呢,这可怎么搞?!

这时候CallAdapter的作用就体现出来了,接口方法可以定义想要的返回类型,然后初始化Retrofit时配置相应的CallAdapterFactory,CallAdapter对方法的返回类型进行适配,让Retrofit能正常操控网络请求的核心类OkHttpCall进行网络请求,并在请求完成后返回适配后的对象。如Retrofit的BodyCallAdapter:

private static final class BodyCallAdapter<R> implements CallAdapter<R, CompletableFuture<R>> {
    private final Type responseType;

    BodyCallAdapter(Type responseType) {
      this.responseType = responseType;
    }

    @Override public Type responseType() {
      return responseType;
    }

    @Override public CompletableFuture<R> adapt(final Call<R> call) {
      final CompletableFuture<R> future = new CallCancelCompletableFuture<>(call);

      call.enqueue(new Callback<R>() {
        @Override public void onResponse(Call<R> call, Response<R> response) {
          if (response.isSuccessful()) {
            future.complete(response.body());
          } else {
            future.completeExceptionally(new HttpException(response));
          }
        }

        @Override public void onFailure(Call<R> call, Throwable t) {
          future.completeExceptionally(t);
        }
      });

      return future;
    }
  }
复制代码

接口方法发起网络请求实际上是操控OkHttpCall去请求,而只有Call才能去调用OkHttpCall(因为OkHttpCall实现的是Call接口),现在我期望通过CompletableFuture去完成请求工作,那么调用这个API方法的时候Retrofit就会根据方法的原始返回类型构建一个Call对象出来交给CallAdapter,CallAdapter根据这个Call对象去构建CompletableFuture对象,并利用Call去执行请求,最终将请求结果封装进CompletableFuture对象并返回。也就是说,不管API方法返回什么类型,实际上都是由Call去执行请求的, 而经过CallAdapter的适配则让任何返回类型的方法都能实现网络请求,就好比游乐园入口处的检票员只认门票不认钱,只要你有门票就能进,你想要进行网络请求就必须持有门票Call,而CallAdapter则类似于售票处,把不同形式的钱兑换成门票。

sequenceDiagram
Retrofit->>Call:构建
Call->>自定义类型:构建(CallAdapter)
自定义类型->>调用者:持有
调用者->>自定义类型:调用
自定义类型->>Call:调用
Call->>Retrofit:执行网络请求
Retrofit->>Call:返回结果
Call->>自定义类型:返回结果
自定义类型->>调用者:返回结果

好了现在我想要在API里加一个返回类型为Observable的方法:

interface TestApi {
    @GET("/.../...")
    fun getDataA(): Call<DataBean>
    
    @GET("/.../...")
    fun getDataB(): Observable<DataBean>
}
复制代码

Retrofit本身有提供RxJavaCallAdapterFactory,初始化时直接配置这个适配器工厂之后就可以正常调用getDataB了,也就是说每个接口方法都需要有一个能适配其返回类型的CallAdapter的,那调用的时候Retrofit怎么判断由哪个CallAdapter来工作呢?继续扒一扒Retrofit是怎么检索CallAdapter的。

CallAdapter的创建和检索

创建Retrofit实例的时候是通过build方法来设置配置的,在build方法中会把手动添加的CallAdapterFactory加入网络请求适配器工厂集合中:

public Retrofit build() {
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
      ......//省略无关代码
}
复制代码

在生成接口方法的ServiceMethod对象时会调用到HttpServiceMethod的parseAnnotations方法,而对应这个方法的CallAdapter对象也是在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) {
      ......//Kotlin协程检测
      
      adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
      annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
    } else {
      adapterType = method.getGenericReturnType();
    }

    CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
    Type responseType = callAdapter.responseType();
    ......//省略无关代码
}
复制代码

parseAnnotations方法中先是获取了接口方法的返回类型adapterType,并传进createCallAdapter里生成并返回接口方法的CallAdapter对象,继续进入createCallAdapter方法:

private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
      Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {
    try {
      //noinspection unchecked
      return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
    } catch (RuntimeException e) { // Wide exception range because factories are user code.
      throw methodError(method, e, "Unable to create call adapter for %s", returnType);
    }
  }
复制代码

可以看到实际上调用了Retrofit的callAdapter方法,继续点进去看看到底干了什么:

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

Retrofit.callAdapter里调用了nextCallAdapter这个方法,检索CallAdapter的实际调用链路为loadServiceMethod -> ServiceMethod.parseAnnotations -> HttpServiceMethod.parseAnnotations->HttpServiceMethod.createCallAdapter->Retrofit.callAdapter->Retrofit.nextCallAdapter:

public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
    Objects.requireNonNull(returnType, "returnType == null");
    Objects.requireNonNull(annotations, "annotations == null");

    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;
      }
    }
    ......
}
复制代码

在nextCallAdapter方法里面,根据接口方法的返回类型returnType遍历调用CallAdapter.Factory的get方法,直到找出合适的CallAdapter并返回一个CallAdapter对象。也就说明在创建接口方法的ServiceMethod对象时,会在网络请求适配器工厂集合里根据方法的返回类型检索出合适的CallAdapter,并把这个CallAdapter对象封装进ServiceMethod对象中。

分类:
Android
标签: