千家信息网

Retrofit之OKHttpCall源码的示例分析

发表于:2024-11-20 作者:千家信息网编辑
千家信息网最后更新 2024年11月20日,这篇文章主要介绍Retrofit之OKHttpCall源码的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!之前在Retrofit源码初探一文中我们提出了三个问题:什么时
千家信息网最后更新 2024年11月20日Retrofit之OKHttpCall源码的示例分析

这篇文章主要介绍Retrofit之OKHttpCall源码的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!

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

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

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

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

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

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

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

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

final class OkHttpCall implements Call {

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

public interface Call extends Cloneable { Response execute() throws IOException; void enqueue(Callback callback); boolean isExecuted(); void cancel(); boolean isCanceled(); Call 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[] handlers = (ParameterHandler[]) 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 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 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()中进行的。

以上是"Retrofit之OKHttpCall源码的示例分析"这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注行业资讯频道!

方法 生成 对象 信息 就是 代码 问题 参数 注解 源码 分析 接口 级别 处理 核心 篇文章 示例 特殊 两个 内容 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 营业厅网络安全攻防演练 为群众办实事宣传网络安全 app软件出现没有可用服务器 时事新闻网络安全知识观后感 软件开发无基础公司 轻云服务器数据库大小 近几十年出现的网络技术词语 政府招商投资平台软件开发商 瑞星网络安全工程师认证 数据通信与IP网络技术知识 hprd数据库 在北京做软件开发月薪多少 违反网络安全法第十五条 自己搭建web服务器 收费站网络安全组织机构及职责 csgo连接不上国服服务器 手机通话后显示服务器错误 澳新成都分部软件开发 网络安全网络攻防论文摘要 江阴品牌软件开发销售电话 tbc各服务器在线人数 收款人支付宝网络技术有限公司 山西常规软件开发预算 数据库管理实施细则 网络安全保险发展的优势 江西派啦网络技术有限公司 行业软件开发培训机构 河北靠谱虚拟主机云空间服务器 新媒体网络安全新闻报道 完美服务器一直断开连接
0