挖一挖Retrofit源码(二)

120 阅读5分钟

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

Retrofit源码系列文章:

在上一篇挖一挖Retrofit源码(一)分析了Retrofit的初始化流程和创建动态代理对象的逻辑,本文将继续介绍Retrofit的请求和回调处理。

PS:本文基于Retrofit版本2.8.0

请求和回调处理

当接口方法被调用的时候,是通过动态代理对象调用invoke方法来发起请求的,实际的调用链路为loadServiceMethod.invoke -> ServiceMethod.invoke -> HttpServiceMethod.invoke

@Override final @Nullable ReturnT invoke(Object[] args) {
    Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    //传入OkHttpCall实例发起请求
    return adapt(call, args);
  }

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

HttpServiceMethod的invoke方法里生成了一个OkHttpCall实例,并传进了①发起请求,前面2.3中HttpServiceMethod对象的生成是分了三种情况的,这里的adapt方法的具体实现也相对应分了三种情况:

  • 接口方法不使用协程:adapt方法在HttpServiceMethod的子类CallAdapted中实现
  • 接口方法挂起且返回类型为Response:adapt方法在HttpServiceMethod的子类SuspendForResponse中实现
  • 接口方法挂起且为普通返回类型:adapt方法在HttpServiceMethod的子类SuspendForBody中实现

下面继续分情况来具体分析

1. 非挂起方法请求

查看一下CallAdapted中的adapt方法源码:

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

可以看到,非挂起方法发起请求时实际上调用的是CallAdapter中的adapt方法,没有配置CallAdapter的话adapt方法默认是在DefaultCallAdapterFactory中实现的:

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

adapt方法实际上返回了DefaultCallAdapterFactory.ExecutorCallbackCall(executor, call))对象,继续点进去ExecutorCallbackCall里看看是啥东东:

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>() {
                ......
            });
        }
        
        @Override public Response<T> execute() throws IOException {
      return delegate.execute();
    }
    ......
}

入参delegate从上面已经知道了实际传进的是OkHttpCall,而非挂起方法进行异步请求时是调用了enqueue方法,同步请求则调用execute方法,也就是实际上请求和回调处理工作是由OkHttpCall.enqueue/OkHttpCall.execute完成的,ExecutorCallbackCall只是OkHttpCall的一个代理类。

1.1 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;
      failure = creationFailure;
      if (call == null && failure == null) {
        try {
          call = rawCall = createRawCall(); //①
        } catch (Throwable t) {
          ......//异常处理
        }
      }
    }

    ......//省略生成call失败和取消的处理
    
    //②
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
        Response<T> response;
        try {
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          ......//异常处理
        }

        try {
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          ......//异常处理
        }
      }

      @Override public void onFailure(okhttp3.Call call, IOException e) {
        callFailure(e); //请求失败处理
      }
    });
  }

enqueue方法主要做了这些工作:

  • ①处在网络请求前创建了OkHttp的Request对象,再将其封装成OkHttp.call
  • ②处通过OkHttp的Call发起异步请求,在请求成功的回调里调用parseResponse将返回的数据解析成Retrofit的Response,最后将Response通过Callback.onResponse回调出去
  • 请求失败则抛出异常

下面继续看看parseResponse这个方法是怎么把数据解析成接口方法所需的返回类型的。

1.2 OkHttpCall类parseResponse方法

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

    rawResponse = rawResponse.newBuilder()
        .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
        .build();

    int code = rawResponse.code();
    ......//根据状态码处理成功和异常的情况
    
    //创建ResponseBody的代理对象
    ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
    try {
      T body = responseConverter.convert(catchingBody);
      return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
      ......//异常处理
    }
  }

parseResponse方法里根据OkHttp返回结果类型生成了对应的响应体rawResponse,然后根据状态码对成功和异常的情况进行分别处理,并创建了一个ResponseBody的代理对象catchingBody,创建Retrofit实例时配置的数据转换器(GsonConverterFactory)通过代理对象catchingBody读取数据流将返回的数据转换成接口方法的返回类型(DataBean),然后把转换后的结果封装成OkHttp的Response类型并返回,最终在enqueue方法里调用Callback.onResponse将结果再封装成Retrofit的Response类型回调返回。

1.3 OkHttpCall类execute方法

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

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

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

    return parseResponse(call.execute());
  }

同步请求的逻辑很简单,先创建出一个OkHttp的Request对象封装成OkHttp.Call,然后调用call.execute发起网络请求,并解析返回数据封装成Response类型返回。

2. 普通返回类型的挂起方法请求

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

普通返回类型的挂起方法发起请求时实际上调用的是SuspendForBody中的adapt方法,方法内部先是走了一遍callAdapter.adapt创建出一个Retrofit的Response类型对象call,返回数据可空则调用KotlinExtensions.awaitNullable,否则调用KotlinExtensions.await来发起请求,awaitNullable方法和await方法的逻辑大同小异,下面看await方法即可:

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) {
            ......//响应体为空的异常处理
            continuation.resumeWithException(e)
          } else {
            continuation.resume(body)
          }
        } else {
          continuation.resumeWithException(HttpException(response))
        }
      }

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

suspendCancellableCoroutine会挂起协程,然后在enqueue方法里发起请求,这里实际上执行的是OkHttpCall的enqueue,使用OkHttp发起网络请求,请求成功并且回复成功则调用resume方法恢复挂起并返回数据,否则调用resumeWithException恢复挂起并抛出错误。

3. 返回类型为Response的挂起方法请求

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

返回类型为Response的挂起方法发起请求时实际上调用的是SuspendForResponse中的adapt方法,同样会先走一遍callAdapter.adapt,逻辑跟2中的大同小异,此处不展开分析了。

流程

Retrofit的简单调用流程:

image.png

PS:本文仅代表个人理解看法,如有不对的地方欢迎各位大佬不吝赐教