OKHttp3--调用对象RealCall源码解析【四】_没有鱼了的博客-CSDN博客

一:概述,当我们封装好 Request后需要执行这个请求,但是 OkHttp并不是直接执行 Request ,而是将 Request又封装了一层 Call对象,一个Call对象代表一个已经准备好的请求(Request), Call可以取消,同时一个 Call对象代表了一个 请求/响应 (request/response)流 。

二:Call只是一个请求接口,具体的实现是 RealCall ,不管是同步请求还是异步请求,请求的对象均是RealCall . 当我们执行同步请求时会调用它的execute方法,执行异步请求会调用enqueue方法

三:RealCall源码

RealCall的代码并不复杂,也没有做过多的操作,而且同步异步的请求在RealCall中的操作基本差不多,只不过同步请求是在当前线程执行,而异步请求是封装了一个子线程AsyncCall去执行请求;而关于具体的网络请求过程都在拦截器里进行

/*** 一个Call封装一对Request和Response,能且仅能被执行一次。并且Call可以被取消。*/
final class RealCall implements Call {//OKHttp客户端final OkHttpClient client;//重试和重定向拦截器final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;//RealCall状态监听器final EventListener eventListener;//请求对象final Request originalRequest;final boolean forWebSocket;// RealCall是否执行过private boolean executed;RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {final EventListener.Factory eventListenerFactory = client.eventListenerFactory();this.client = client;this.originalRequest = originalRequest;this.forWebSocket = forWebSocket;this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);this.eventListener = eventListenerFactory.create(this);}/** 返回初始化此Call的原始请求 */@Override public Request request() {return originalRequest;}/** 进行同步请求* 立即发出请求,一直阻塞当前线程,直到返回结果或者报错* 可以使用Response.body获取结果* 为了复用连接,需要调用Response.close关闭响应体*/@Override public Response execute() throws IOException {synchronized (this) {//同一个RealCall,只能执行一次if (executed) throw new IllegalStateException("Already Executed");executed = true;}//打印堆栈信息captureCallStackTrace();try {//加入分发器中的正在执行的同步请求队列client.dispatcher().executed(this);//获取请求结果Response result = getResponseWithInterceptorChain();if (result == null) throw new IOException("Canceled");return result;} finally {//通知分发器请求完成,从队列移除client.dispatcher().finished(this);}}private void captureCallStackTrace() {Object callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()");retryAndFollowUpInterceptor.setCallStackTrace(callStackTrace);}/*** 进行异步请求* 至于何时执行这个请求由分发器决定* 通常是立即执行,除非当前有任务在执行或不符合限制条件* 如果不能立即执行,会被保存到等待执行的异步请求队列* 请求结束后,会通过回调接口将结果返回*/@Override public void enqueue(Callback responseCallback) {synchronized (this) {if (executed) throw new IllegalStateException("Already Executed");executed = true;}captureCallStackTrace();//实例化一个线程AsyncCall交给分发器,由分发器中的线程池执行这个线程client.dispatcher().enqueue(new AsyncCall(responseCallback));}//取消这个RealCall 如果请求已经有返回了,那么就不能被取消了@Override public void cancel() {retryAndFollowUpInterceptor.cancel();}//判断是否执行过@Override public synchronized boolean isExecuted() {return executed;}//判断是否取消了@Override public boolean isCanceled() {return retryAndFollowUpInterceptor.isCanceled();}//复制一个相同的RealCall@SuppressWarnings("CloneDoesntCallSuperClone")@Override public RealCall clone() {return new RealCall(client, originalRequest, forWebSocket);}//StreamAllocation协调Connections,Streams,Calls三者之间的关系StreamAllocation streamAllocation() {return retryAndFollowUpInterceptor.streamAllocation();}//异步请求线程final class AsyncCall extends NamedRunnable {private final Callback responseCallback;AsyncCall(Callback responseCallback) {super("OkHttp %s", redactedUrl());this.responseCallback = responseCallback;}//主机名String host() {return originalRequest.url().host();}Request request() {return originalRequest;}RealCall get() {return RealCall.this;}//当分发器的线程池执行该对象时,该方法被调用@Override protected void execute() {//保证onFailure只被回调一次boolean signalledCallback = false;try {//通过拦截器获取返回结果Response response = getResponseWithInterceptorChain();if (retryAndFollowUpInterceptor.isCanceled()) {//如果请求被取消,回调onFailuresignalledCallback = true;responseCallback.onFailure(RealCall.this, new IOException("Canceled"));} else {// 正常情况,调用onResponsesignalledCallback = true;responseCallback.onResponse(RealCall.this, response);}} catch (IOException e) {// 如果上面回调过,这里就不再进行回调,保证onFailure只会被调用一次if (signalledCallback) {Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);} else {responseCallback.onFailure(RealCall.this, e);}} finally {//通知分发器请求结束,从队列中移除该请求client.dispatcher().finished(this);}}}/*** Returns a string that describes this call. Doesn't include a full URL as that might contain* sensitive information.*/String toLoggableString() {return (isCanceled() ? "canceled " : "")+ (forWebSocket ? "web socket" : "call")+ " to " + redactedUrl();}//返回包含此URL的字符串,无用户名,密码,查询信息String redactedUrl() {return originalRequest.url().redact();}//依次执行拦截器链中的拦截器获取结果Response getResponseWithInterceptorChain() throws IOException {List<Interceptor> interceptors = new ArrayList<>();//添加自定义拦截器interceptors.addAll(client.interceptors());//添加重试和重定向拦截器interceptors.add(retryAndFollowUpInterceptor);//添加桥接拦截器interceptors.add(new BridgeInterceptor(client.cookieJar()));//添加缓存拦截器interceptors.add(new CacheInterceptor(client.internalCache()));//添加链接拦截器interceptors.add(new ConnectInterceptor(client));if (!forWebSocket) {//添加网络拦截器interceptors.addAll(client.networkInterceptors());}//添加连接服务器拦截器,主要负责将我们的Http请求写进网络的IO流中interceptors.add(new CallServerInterceptor(forWebSocket));//构建拦截器链依次执行每一个拦截器Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest);return chain.proceed(originalRequest);}
}

五:异步请求

异步请求会走到内部的enqueue方法

  @Override public void enqueue(Callback responseCallback) {synchronized (this) {if (executed) throw new IllegalStateException("Already Executed");executed = true;}captureCallStackTrace();// 实例化 AsyncCall对象client.dispatcher().enqueue(new AsyncCall(responseCallback));}

首先它是RealCall的内部类,我们都知道异步请求是在子线程执行的,但是到这里我们还没有看出来子线程的影子,那我们就需要看下RealCall的父类了

  final class AsyncCall extends NamedRunnable {private final Callback responseCallback;AsyncCall(Callback responseCallback) {super("OkHttp %s", redactedUrl());this.responseCallback = responseCallback;}String host() {return originalRequest.url().host();}Request request() {return originalRequest;}RealCall get() {return RealCall.this;}@Override protected void execute() {boolean signalledCallback = false;try {Response response = getResponseWithInterceptorChain();if (retryAndFollowUpInterceptor.isCanceled()) {signalledCallback = true;responseCallback.onFailure(RealCall.this, new IOException("Canceled"));} else {signalledCallback = true;responseCallback.onResponse(RealCall.this, response);}} catch (IOException e) {if (signalledCallback) {// Do not signal the callback twice!Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);} else {responseCallback.onFailure(RealCall.this, e);}} finally {client.dispatcher().finished(this);}}}

NamedRunnable实现了Runnable接口,从这里可以看出AsyncCall确实是在子线程执行网络请求

public abstract class NamedRunnable implements Runnable {protected final String name;public NamedRunnable(String format, Object... args) {this.name = Util.format(format, args);}@Override public final void run() {String oldName = Thread.currentThread().getName();Thread.currentThread().setName(name);try {execute();} finally {Thread.currentThread().setName(oldName);}}protected abstract void execute();
}

六:同步请求

同步请求会走到内部的execute方法

第一步:首先出现一个同步代码块,对当前对象加锁,通过一个标志位executed判断该对象的execute方法是否已经执行过,如果执行过就抛出异常;这也就是同一个Call只能执行一次的原因
      第二步:这个是用来捕获okhttp的请求堆栈信息,不是重点
      第三步:调用Dispatcher的executed方法,将请求放入分发器,这是非常重要的一步
      第四步:通过拦截器连获取返回结果Response
     第五步:调用dispatcher的finished方法,回收同步请求

  @Override public Response execute() throws IOException {//第一步synchronized (this) {if (executed) throw new IllegalStateException("Already Executed");executed = true;}//第二步captureCallStackTrace();try {//第三步client.dispatcher().executed(this);//第四步Response result = getResponseWithInterceptorChain();if (result == null) throw new IOException("Canceled");return result;} finally {//第五步client.dispatcher().finished(this);}}

第三步和第五步都在分发器内部处理,我们重点看下 第四步

这里实例化一个List,用来存放Interceptor对象,主要是OKHttp提供的5种拦截器

接下来实例化了一个拦截器链对象,在这个拦截器链里依次执行每个拦截器

  Response getResponseWithInterceptorChain() throws IOException {// Build a full stack of interceptors.List<Interceptor> interceptors = new ArrayList<>();interceptors.addAll(client.interceptors());interceptors.add(retryAndFollowUpInterceptor);interceptors.add(new BridgeInterceptor(client.cookieJar()));interceptors.add(new CacheInterceptor(client.internalCache()));interceptors.add(new ConnectInterceptor(client));if (!forWebSocket) {interceptors.addAll(client.networkInterceptors());}interceptors.add(new CallServerInterceptor(forWebSocket));Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest);return chain.proceed(originalRequest);}

网络请求框架:Okhttp:Call对象实现请求源码解析【四】相关推荐

  1. String str = new String(abc)创建了几个对象?结合源码解析

    String str = new String("abc")创建了几个对象?结合源码解析 首先,我们看一下jdk源码: 1 /** 2 * Initializes a newly ...

  2. android之网络请求框架OKhttp、原生http请求

    ===============原生http===================== public class MainActivity extends AppCompatActivity imple ...

  3. [源码解析] 深度学习分布式训练框架 horovod (10) --- run on spark

    [源码解析] 深度学习分布式训练框架 horovod (10) - run on spark 文章目录 [源码解析] 深度学习分布式训练框架 horovod (10) --- run on spark ...

  4. [源码解析] 深度学习分布式训练框架 horovod (11) --- on spark --- GLOO 方案

    [源码解析] 深度学习分布式训练框架 horovod (11) - on spark - GLOO 方案 文章目录 [源码解析] 深度学习分布式训练框架 horovod (11) --- on spa ...

  5. axios网络请求框架源码解析

    早期axios0.1.0版本做了对IE浏览器与包含XmlHttpRequest的浏览器的支持.并且做了对请求参数拼接.Json对象序列化等基本功能. 到0.19.0版本时,内部请求已经变为了在Node ...

  6. Volley 源码解析之网络请求

    Volley源码分析三部曲 Volley 源码解析之网络请求 Volley 源码解析之图片请求 Volley 源码解析之缓存机制 Volley 是 Google 推出的一款网络通信框架,非常适合数据量 ...

  7. Java生鲜电商平台-SpringCloud微服务架构中网络请求性能优化与源码解析

    Java生鲜电商平台-SpringCloud微服务架构中网络请求性能优化与源码解析 说明:Java生鲜电商平台中,由于服务进行了拆分,很多的业务服务导致了请求的网络延迟与性能消耗,对应的这些问题,我们 ...

  8. Android 网络编程之OkHttp源码解析

    前言:OkHttp框架是Android的网络请求框架,无数的项目都在使用着这个框架,重要性不言而喻; 本文会将OKHTTP的源码进行拆解,每个部分来单独学习,由简入深,循序渐进,篇幅较长,建议收藏,慢 ...

  9. Android开发神器:OkHttp框架源码解析

    前言 HTTP是我们交换数据和媒体流的现代应用网络,有效利用HTTP可以使我们节省带宽和更快地加载数据,Square公司开源的OkHttp网络请求是有效率的HTTP客户端.之前的知识面仅限于框架API ...

最新文章

  1. 关于linux基础的博客,第一篇博客,以下。
  2. 图灵访谈 | 王贝珊:乘风破浪的成长之路
  3. VS2017 快捷键
  4. 已完结 | PMCAFF深度报告:《 典典养车如何在一年内做到600万用户、三轮融资、覆盖40城市》
  5. c判断char数组是否为空_你学过数组,那你知道柔性数组吗?
  6. i/o传输数据打印换行符的方法
  7. python flask源码解析_Flask知识全套及源码分析
  8. Ubuntu 系统调整LVM卷/home分区到 / 分区
  9. 网络-1集线器/交换机/路由器
  10. 让餐饮店生意火爆的三套方案
  11. node.js 使用domain模块捕获异步回调中的异常
  12. Linux文件及文件内容的查找-转
  13. 十进制转5421BCD(测试文件及modelsim.do文件的模板)——补充篇
  14. 西南交通大学计算机应用基础,西南交通大学计算机应用基础作业-客观部分
  15. keil系列 魔法棒(目标选项)配置、编译工具ARMCC
  16. PMP课程笔记:第6章 项目进度管理
  17. 一分钟了解QPS TPS RPS
  18. [4G5G专题-91]:流程 - 4G LTE 终端移动性管理总体概述
  19. linux下Hiredis的安装和简单使用
  20. sshpass命令的安装使用

热门文章

  1. Outlook设置Gmail邮箱步骤
  2. 向后切片,正向切片和其他形式的切片
  3. 机器学习:准确率(Precision)、召回率(Recall)、F值(F-Measure)、ROC曲线、PR曲线
  4. 金融术语----持仓数量
  5. Acrel-5000型建筑能耗监测可对商业建筑用电及用水的监测分析
  6. 跨境电商亚马逊这个行业还能赚钱吗,行业未来在哪里?
  7. Waifu2x-Extension-GUI 图片 GIF 视频 人工智能放大与降噪(超分辨率) v1.50.2 更新
  8. 超强图解Pandas
  9. pip install skimage安装skimage库出错问题
  10. 腾讯首度公开S级手游品质管理方法