Retrofit是如何支持Kotlin协程的?

3,861 阅读4分钟

前言

相信很多小伙伴在使用了Kotlin+协程之后,小伙伴们都是一副这个样子! Retrofit对于协程的支持,是在2.6.0版本之后才开始支持的,在此之前对于在Retrofit中使用协程,主要靠的是Jake Wharton写的开源库retrofit2-kotlin-coroutines-adapter 来支持协程。

本篇的目的不是为了和大家详细讨论Retrofit源码,而是抓住其中的协程相关部分,同时与J神的retrofit2-kotlin-coroutines-adapter 进行一点比较(虽然两个东西都是他写的),来让大家了解,Retrofit是如何实现对协程的支持的。

如果大家对与协程不是太了解,推荐凯哥的码上开学免费课程,当然,最好的是通过官方文档进行学习。

Retrofit的基本思路

Retrofit是一个最典型的动态代理实现对象,通过接口的方法,来为我们生成真正可以进行网络请求的一个对象,然后我们再通过这个方法,获取返回值,完成我们的网络请求。

此处为动态代理的逻辑,也是Retrofit的实现入口:

//这个方法就是我们如何通过动态代理,将一个接口类,转换为一个可以进行网络请求的类
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 (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args);
                }
                args = args != null ? args : emptyArgs;
                return platform.isDefaultMethod(method)
                    ? platform.invokeDefaultMethod(method, service, proxy, args)
                    //动态代理之后,真正调用的方法
                    : loadServiceMethod(method).invoke(args);
              }
            });
  }

请大家记住这个loadServiceMethod(method).invoke(args)方法

CallAdapter<T, R>转换器

因为我们知道,Retrofit其实是对OkHttp的封装,从这个角度分析,我们需要了解的是,Retrofit是如何将Okhttp的Call 转换为Kotlin的协程的挂起函数

//默认CallAdapter添加逻辑Retrofit.Builder().build()中实现  
public Retrofit build() {
      // Make a defensive copy of the adapters and add the default Call adapter.
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

    
      return new Retrofit(
          callFactory,
          baseUrl,
          unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories),
          callbackExecutor,
          validateEagerly);
    }
  }
  
  //这个是Platform中的方法
  List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
      @Nullable Executor callbackExecutor) {
    DefaultCallAdapterFactory executorFactory = new DefaultCallAdapterFactory(callbackExecutor);
    return hasJava8Types
        ? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
        : singletonList(executorFactory);
  }
  
  //
  @Override
  public @Nullable CallAdapter<?, ?> get(
      Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    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);

    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);
      }
    };
  }

在获取到Call之后,就进入处理的流程了,如果不使用@SkipCallbackExecutor注解,默认会调用会返回ExecutorCallbackCall,也就是后台线程执行能力的一个Call,这个Call大家比较熟悉了。

HttpServiceMethod<ResponseT, ReturnT>

通过跟随动态代理的逻辑,代码会点击到HttpServiceMethod<ResponseT, ReturnT>,这个类的作用就是,怎么返回你在接口类中声明的返回值

  static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
    boolean continuationWantsResponse = false;
    boolean continuationBodyNullable = false;
	、、、、、、、、省略一些代码
  //注解1
    CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
    Type responseType = callAdapter.responseType();
    if (responseType == okhttp3.Response.class) {
      throw methodError(
          method,
          "'"
              + getRawType(responseType).getName()
              + "' is not a valid response body type. Did you mean ResponseBody?");
    }
    if (responseType == Response.class) {
      throw methodError(method, "Response must include generic type (e.g., Response<String>)");
    }
    // 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.");
    }

    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);

    okhttp3.Call.Factory callFactory = retrofit.callFactory;
  
    if (!isKotlinSuspendFunction) {
      return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    } else if (continuationWantsResponse) {
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
  	  //注解2
      return (HttpServiceMethod<ResponseT, ReturnT>)
          new SuspendForResponse<>(
              requestFactory,
              callFactory,
              responseConverter,
              (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
    } else {
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
      //注解3
      return (HttpServiceMethod<ResponseT, ReturnT>)
          new SuspendForBody<>(
              requestFactory,
              callFactory,
              responseConverter,
              (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
              continuationBodyNullable);
    }
  }
  

这里有几个关键的参数,isKotlinSuspendFunction区分是否为挂起函数,continuationWantsResponse是否返回体是Response 注解1,也就是我们上面讲到的Adapter,作用就是返回Call,细节不用太深究,主要讲讲HttpServiceMethod的子类SuspendForBody,也就是真正将响应体转换为挂起函数的过程。

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

此处的invoke函数,也就是我们在代理的实现中,去调用的方法,调用adapt的方法,并传入我们的通过通过解析接口方法,获取到的Call,再调用SuspendForBody.adapt(Call call, Object[] args)

  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);
      }
    }
  }

再通过Kotlin扩展函数

  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()
          if (body == null) {
            val invocation = call.request().tag(Invocation::class.java)!!
            val method = invocation.method()
            val e = KotlinNullPointerException("Response from " +
                method.declaringClass.name +
                '.' +
                method.name +
                " was null but response body type was declared as non-null")
            continuation.resumeWithException(e)
          } else {
            continuation.resume(body)
          }
        } else {
          continuation.resumeWithException(HttpException(response))
        }
      }

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

最终返回一个挂起函数,并通过continuation,对函数进行挂起、恢复、抛出异常和作用域关闭时协程的中断管理。

结语

可能用很大一部分的开发者,已经用上协程+Jetpack+MVVM这一套的东西了。自己最近也在新项目上尝试,使用新的一个技术栈。网上的相关博客,也查阅了很多,但总是不和我意,也可能因为我没达到别人的境界吧。就是希望大家可以在懂原理的基础上,去使用一些框架。 然后对于文中有不足或者错误的,请大家帮忙指正。 本文纯粹个人的拙见,望各位同好们多多包涵