Retrofit之OKHttpCall源码分析

作者:低情商的大仙 时间:2021-11-13 22:08:53 

之前在Retrofit源码初探一文中我们提出了三个问题:

  1. 什么时候开始将注解中参数拼装成http请求的信息的?

  2. 如何产生发起http请求对象的?

  3. 如何将对象转换成我们在接口中指定的返回值的?

其中第一个问题前几篇文章已经做了解答,今天我们探究下第二个问题。

之前也分析过,具体生成这个请求对象的是这句代码:


OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);

代码很简单,那我们就来探究下这个OkHttpCall能干什么:


final class OkHttpCall<T> implements Call<T> {

可以看到其实主要实现了一个接口,所以我们看下这个接口都有哪些方法:


public interface Call<T> extends Cloneable {
Response<T> execute() throws IOException;
void enqueue(Callback<T> callback);
boolean isExecuted();
void cancel();
boolean isCanceled();
Call<T> clone();
/** The original HTTP request. */
Request request();
}

看到这几个方法有没有很熟悉,没错,几乎和Okhttp的Call方法一模一样,我们看下okhttp的call接口:


public interface Call extends Cloneable {
Request request();

Response execute() throws IOException;

void enqueue(Callback var1);

void cancel();

boolean isExecuted();

boolean isCanceled();

Call clone();

public interface Factory {
 Call newCall(Request var1);
}
}

从这里我们猜测,Retrofit的OkHttpCall其实就是对OkHttp的call的一种包装,下面我们详细探究下每种方法,看是如何分别调用OkHttp的call中的方法的,有没有做什么特殊处理。

之前有提过看源码之前要带着问题去看,那么对于这个OkHttpCall我们想知道什么?之前提到过这是对OkHttp的okhttp3.Call的一个封装,那么每个方法必然会调用到okhttp3.Call对应的方法,所以我们提出两个问题:

  1. 这个类中okhttp3.Call对象是怎么生成的?

  2. 调用okhttp3.Call中对应的方法时有没有做什么特殊操作?

这两个问题在每个主要方法中都能得到答案。

request()方法


@Override public synchronized Request request() {
okhttp3.Call call = rawCall;
if (call != null) {
 return call.request();
}
if (creationFailure != null) {
 if (creationFailure instanceof IOException) {
 throw new RuntimeException("Unable to create request.", creationFailure);
 } else if (creationFailure instanceof RuntimeException) {
 throw (RuntimeException) creationFailure;
 } else {
 throw (Error) creationFailure;
 }
}
try {
 return (rawCall = createRawCall()).request();
} catch (RuntimeException | Error e) {
 throwIfFatal(e); // Do not assign a fatal error to creationFailure.
 creationFailure = e;
 throw e;
} catch (IOException e) {
 creationFailure = e;
 throw new RuntimeException("Unable to create request.", e);
}
}

可以看到,大致逻辑就是如果okhttp3.Call已经被实例化了直接调用它的request()方法,如果没有的话,会调用createRawCall()方法先实例化,然后再调用request方法。

所以想要解答okhttp3.Call是怎么生成的,就来看看这个createRawCall()方法:


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

可以看到核心方法还是ServiceMethod中的toCall方法来生成的,这里提供了参数而已,继续跟进去:


okhttp3.Call toCall(@Nullable Object... args) throws IOException {
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
 contentType, hasBody, isFormEncoded, isMultipart);

@SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;

int argumentCount = args != null ? args.length : 0;
if (argumentCount != handlers.length) {
 throw new IllegalArgumentException("Argument count (" + argumentCount
  + ") doesn't match expected count (" + handlers.length + ")");
}

for (int p = 0; p < argumentCount; p++) {
 handlers[p].apply(requestBuilder, args[p]);
}

return callFactory.newCall(requestBuilder.build());
}

这个方法最终其实就是调用okhttp3.Call中的这个方法:


public interface Factory {
 Call newCall(Request var1);
}

至于怎么根据Request生成Call是OkHttp干的,在ServiceMethod中的toCall方法,我们要做的就是用已有信息生成一个OkHttp的Request来,如何生成这个Request?这里利用了一个RequesetBuilder。

第一:处理方法级别的注解的信息

利用httpMethod,baseUrl,relativeUrl等直接new了一个RequestBuilder出来,这些信息都是从方法级别的注解中解析出来的。
第二:处理参数级别的注解信息

之前在生成ServiceMethod对象时,利用参数级别的注解生成了一个ParameterHandler数组,每个Handler都有一个apply方法,将参数信息设置到一个RequestBuilder中,这个apply方法就是在这里调用的。

经过上面两部,一个包含了http请求完整信息的RequesetBuilder就生成了,最后build下生成一个Request传到newCall方法中,则一个okhttp3.Call对象就生成了。

整个request()方法分析完了,做的事很简单,有okhttp3.Call对象就直接调用它的request()方法,没有就生成一个再调用,但大家注意到没有,他的代码设计安排很奇怪。如果是我来写这个方法,我可能会这样写:


public synchronized Request request1() {
okhttp3.Call call = rawCall;
if (call != null) {
 return call.request();
}else{
 try {
 return (rawCall = createRawCall()).request();
 } catch (RuntimeException | Error e) {
 throwIfFatal(e); // Do not assign a fatal error to creationFailure.
 throw e;
 } catch (IOException e) {
 throw new RuntimeException("Unable to create request.", e);
 }
}
}

可以看到,和我自己的代码相比,原代码多了一个记录createRawCall()的异常的成员变量,这是处于效率考虑。由于我们的okhtt3.Call对象是延迟加载的,就是说在调用request方法时,其他的方法中有可能已经调用过createRawCall()方法,并由于某种原因失败了,我们将这个失败的异常记录下来,在调用createRawCall()方法之前做一次判断,如果已有异常就不需要调用createRawCall()方法了,提高了效率。

enque()

整个enque()方法的核心必然是调用okhttp3.Call的enque方法,我们重点关注调用之前有做什么,调用之后做了什么:


call.enqueue(new okhttp3.Callback() {
 @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
  throws IOException {
 Response<T> response;
 try {
  response = parseResponse(rawResponse);
 } catch (Throwable e) {
  callFailure(e);
  return;
 }
 callSuccess(response);
 }

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

在调用之前其实没做什么,和request()方法差不多,做了下提前判断而已,所以这里可以直接看代码,核心就是调用了parseResponse()方法将返回值转成了Retrofit的Response对象,然后调用了callSuccess()而已,所以我们跟进去:


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

ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
try {
 T body = serviceMethod.toResponse(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;
}
}

这里逻辑很简单,根据不同的http状态码返回对应的Response对象,这里有一点,当状态码正常时,这里会利用一个converter将Body对象转成自己想要的,比如转成json等,具体处理是在serviceMethod.toResponse()中进行的。

日常偷懒环节

来源:https://www.jianshu.com/p/5cbdafae1a62

标签:Retrofit,OKHttpCall
0
投稿

猜你喜欢

  • Android实现自动点击无障碍服务功能的实例代码

    2022-08-31 12:14:26
  • Java中try、catch的使用方法

    2021-11-08 00:48:12
  • 自定义时间格式转换代码分享

    2022-11-03 03:09:23
  • Spring Cloud之服务监控turbine的示例

    2023-04-20 23:26:44
  • java实现Object转String的4种方法小结

    2023-07-18 08:54:53
  • android 设置全屏的两种方法

    2023-06-30 00:06:11
  • java String的intern方法

    2021-07-05 03:23:52
  • SpringMvc web.xml配置实现原理过程解析

    2023-04-14 01:42:27
  • Java看完秒懂版熔断和降级的关系

    2023-11-06 01:12:13
  • C# Invoke,begininvoke的用法详解

    2023-07-21 01:55:55
  • java.math.BigDecimal的用法及加减乘除计算

    2022-01-15 15:55:20
  • Java中scheduleAtFixedRate的用法

    2022-10-10 11:07:37
  • Android自定义定时闹钟开发

    2023-07-07 00:18:01
  • Spring学习笔记1之IOC详解尽量使用注解以及java代码

    2021-10-07 16:17:02
  • 教你用JAVA写文本编辑器(一)

    2023-06-13 09:05:31
  • Android开发InputManagerService创建与启动流程

    2021-07-11 20:45:22
  • 在maven中引入本地jar包的步骤

    2023-11-25 10:43:44
  • C#使用GDI+实现生成验证码

    2023-04-10 10:42:44
  • Springboot整合微信支付(订单过期取消及商户主动查单)

    2023-05-15 23:40:50
  • 详解Java如何创建Annotation

    2023-05-05 22:40:41
  • asp之家 软件编程 m.aspxhome.com