okhttp源码简单流程分析

拦截器详细解析可以看大佬简书 "https://www.jianshu.com/p/6fac73f7570f"和 “https://www.jianshu.com/p/3c740829475c”

在这里插入图片描述

okhttp请求流程
1:OkHttpClient okHttpClient = new OkHttpClient.Builder()
构建一个okhttpClient对象,传入你想传入的对象,不传就是默认的;
2:构建request对象
Request request = new Request.Builder()
3:okHttpClient.newCall 实际上返回的realCall类 继续调用RealCall.newRealCall
4:调用enqueue方法,传入我们需要的回调接口,而且会判断,
synchronized (this) {
if (executed) throw new IllegalStateException(“Already Executed”);
executed = true;
}
如果当前这个call对象已经被运行的话,则抛出异常;
5:继续调用dispatcher的enqueue方法,如果当前运行队列<64并且正在运行,访问同一个服务器地址的请求<5
就直接添加到运行队列,并且开始运行;
不然就添加到等待队列;
6:运行AsyncCall,调用它的execute方法
7:在execute方法中处理完response之后,会在finally中调用dispathcer的finished方法;
8:当当前已经处理完毕的call从运行队列中移除掉;并且调用promoteCalls方法
9:promoteCalls方法中进行判断,
如果运行队列数目大于等于64,如果等待队列里啥都没有,也直接return?
循环等待队列,
将等待队列中的数据进行移除,移除是根据运行队列中还能存放多少来决定;
移到了运行队列中,并且开始运行;

OkHttpClient okHttpClient = new OkHttpClient.Builder().build();Request request = new Request.Builder().build();//newCall方法是调用RealCall的newRealCall返回一个RealCallCall call = okHttpClient.newCall(request);//执行请求call.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {}@Overridepublic void onResponse(Call call, Response response) throws IOException {}});//这里第二次执行会直接报IllegalStateException错误并提示Already Executedcall.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {}@Overridepublic void onResponse(Call call, Response response) throws IOException {}});

进入call.enqueue方法

@Override public void enqueue(Callback responseCallback) {//这个锁是为了防止重复请求 如果你同一个call enqueue多次就会直接返回Already Executedsynchronized (this) {if (executed) throw new IllegalStateException("Already Executed");executed = true;}captureCallStackTrace();eventListener.callStart(this);//这里的enqueue是传入任务线程AsyncCall并分配到运行队列或等待队列client.dispatcher().enqueue(new AsyncCall(responseCallback));}

进入enqueue方法

//okhttp里执行任务分为两个队列 运行队列和等待队列
synchronized void enqueue(AsyncCall call) {//当运行的队列中的数值小于64, //并且同时访问同一个机器目标HOST请求书小于5直接加入到运行队列不然的话就加入到等待队列if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {runningAsyncCalls.add(call);//这里维护着请求用的线程池executorService().execute(call);} else {readyAsyncCalls.add(call);}}
  public synchronized ExecutorService executorService() {if (executorService == null) {//1:核心线程数 保持在线程池中的线程数量//2:线程池最大可容纳的线程数  //3参数:当线程池中的线程数量大于核心线程数,空闲线程就会等待60s才会被终止,如果小于就会立刻停止;executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));}return executorService;}
//这个就是队列中传入的线程NameRunnable继承自Runnable
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;}//这个就是Async的run方法@Override protected void execute() {boolean signalledCallback = false;try {//getResponseWithInterceptorChain() 添加拦截器 okhttp的责任链设计也在这里//response就是请求结果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 {eventListener.callFailed(RealCall.this, e);responseCallback.onFailure(RealCall.this, e);}} finally {//将执行完成的任务从队列中移除client.dispatcher().finished(this);}}}

添加拦截器

//添加拦截器 责任链设计 
//拦截器详细解析可以看大佬简书 "https://www.jianshu.com/p/6fac73f7570f"和 "https://www.jianshu.com/p/3c740829475c"
Response getResponseWithInterceptorChain() throws IOException {// Build a full stack of interceptors.List<Interceptor> interceptors = new ArrayList<>();interceptors.addAll(client.interceptors());//重试/重定向拦截器 连接失败后进行重试、对请求结果跟进后进行重定向interceptors.add(retryAndFollowUpInterceptor);//桥拦截器:连接应用程序和服务器的桥梁,我们发出的请求会经过它的处理才能发给服务器,//比如设置请求内容的长度 封装header属性 host keep-live gzip header 进行基本设置,interceptors.add(new BridgeInterceptor(client.cookieJar()));//缓存拦截器 在发出请求前,先判断是否命中缓存,//如果命中则可以不请求,直接使用缓存的响应(默认只会对Get请求进行缓存);//如果未命中则进行网络请求,并将结果缓存,等待下次请求被命中。interceptors.add(new CacheInterceptor(client.internalCache()));//连接拦截器 与服务器建立连接。interceptors.add(new ConnectInterceptor(client));if (!forWebSocket) {interceptors.addAll(client.networkInterceptors());}//与服务器通信;封装请求数据与解析响应数据(如:HTTP报文)。interceptors.add(new CallServerInterceptor(forWebSocket));Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,originalRequest, this, eventListener, client.connectTimeoutMillis(),client.readTimeoutMillis(), client.writeTimeoutMillis());//执行拦截器return chain.proceed(originalRequest);}

在execute方法中处理完response之后,会在finally中调用dispathcer的finished方法;

//将当前已经处理完毕的call从运行队列中移除掉;并且调用promoteCalls方法
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {int runningCallsCount;Runnable idleCallback;synchronized (this) {if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");if (promoteCalls) promoteCalls();runningCallsCount = runningCallsCount();idleCallback = this.idleCallback;}if (runningCallsCount == 0 && idleCallback != null) {idleCallback.run();}}

promoteCalls方法中进行判断

//如果运行队列数目大于等于64,如果等待队列里啥都没有,也直接return
//循环等待队列,
//将等待队列中的数据进行移除,移除是根据运行队列中还能存放多少来决定;
//移到了运行队列中,并且开始运行;private void promoteCalls() {if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {AsyncCall call = i.next();if (runningCallsForHost(call) < maxRequestsPerHost) {i.remove();runningAsyncCalls.add(call);executorService().execute(call);}if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/41127.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vector使用以及模拟实现

vector使用以及模拟实现 vector介绍vector常用接口1.构造2.迭代器3.容量4.增删查改5.练习 vector模拟实现1.迭代器失效2.反向迭代器3.完整代码 vector介绍 和我们原来讲的string不同&#xff0c;vector并不是类&#xff0c;是一个类模板&#xff0c;加<类型>实例化以后才…

主机防护的重要性和方式

01 主机防护的重要性 主机防护是网络安全的重要组成部分。在互联网时代&#xff0c;网络攻击成为了一种常见的威胁&#xff0c;而主机防护则是保护计算机系统免受网络攻击的重要手段。 主机防护可以防范各种网络攻击&#xff0c;如病毒、木马、黑客攻击等&#xff0c;从而保…

气象监测站:用科技感知气象变化

气象监测站是利用科学技术感知当地小气候变化情况的气象观测仪器&#xff0c;可用于农业、林业、养殖业、畜牧业、环境保护、工业等多个领域&#xff0c;提高对环境数据的利用率&#xff0c;促进产业效能不断提升。 气象监测站主要由气象传感器、数据传输系统、电源系统、支架…

Linux debian12解压和压缩.rar文件教程

一、Debian12安装rar命令 sudo apt install rar二、使用rar软件 1.解压文件 命令格式&#xff1a; rar x 文件名.rar实力测试&#xff1a; [rootdoudou tmp]# rar x test.rar2.压缩文件 test是一个文件夹 命令格式&#xff1a; rar a 文件名.rar 文件夹名实例测试&#x…

centos7 yum获取软件所有依赖包 创建本地yum源 yum离线安装软件

centos7 yum获取软件所有依赖包 创建本地yum源 离线安装软件 1、以安装docker 20.10为例2、centos7 yum获取docker 20.10 所有依赖包3、创建本地docker yum源4、yum使用本地docker源 离线安装docker 1、以安装docker 20.10为例 参考链接&#xff1a; 添加docker 清华软件源 y…

git环境超详细配置说明

一&#xff0c;简介 在git工具安装完成之后&#xff0c;需要设置一下常用的配置&#xff0c;如邮箱&#xff0c;缩写&#xff0c;以及git commit模板等等。本文就来详细介绍些各个配置如何操作&#xff0c;供参考。 二&#xff0c;配置步骤 2.1 查看当前git的配置 git conf…

使用 Apache Kafka 和 Go 将数据引入 OpenSearch

需要编写自定义集成层来满足数据管道中的特定要求&#xff1f;了解如何使用 Go 通过 Kafka 和 OpenSearch 实现此目的。 可扩展的数据摄取是OpenSearch等大规模分布式搜索和分析引擎的一个关键方面。构建实时数据摄取管道的方法之一是使用Apache Kafka。它是一个开源事件流平台…

米尔瑞萨RZ/G2L开发板-02 ffmpeg的使用和RTMP直播

最近不知道是不是熬夜太多&#xff0c;然后记忆力减退了&#xff1f; 因为板子回来以后我就迫不及待的试了一下板子&#xff0c;然后发现板子有SSH&#xff0c;但是并没有ffmpeg&#xff0c;最近总是在玩&#xff0c;然后今天说是把板子还原一下哇&#xff0c;然后把官方的固件…

前端单点登录SSO面试回答

JWT鉴权机制 1.JWT用于登录身份验证 2.用户登录成功后&#xff0c;后端通过JWT机制生成一个token&#xff0c;返回给客户端 3.客户端后续的每次请求都需要携带token&#xff0c;放在header的authorization中 4.后端从authorization中拿到token后&#xff0c;通过secretKey进…

Spring Boot中使用validator如何实现接口入参自动检验

文章目录 一、背景二、使用三、举例 一、背景 在项目开发过程中&#xff0c;经常会对一些字段进行校验&#xff0c;比如字段的非空校验、字段的长度校验等&#xff0c;如果在每个需要的地方写一堆if else 会让你的代码变的冗余笨重且相对不好维护&#xff0c;如何更加规范和优…

微服务-GateWay(网关)

所谓网关是什么意思&#xff1f; 相当于就是你们小区家的保安&#xff0c;进出小区都得获得保安的同意&#xff0c;守护你们小区的生命财产健康&#xff0c;网关也是如此&#xff0c;对每个请求都严格把关&#xff0c;将合法的或者是获得权限的请求进入服务器 网关的功能&…

扩散模型实战(四):从零构建扩散模型

推荐阅读列表&#xff1a; 扩散模型实战&#xff08;一&#xff09;&#xff1a;基本原理介绍 扩散模型实战&#xff08;二&#xff09;&#xff1a;扩散模型的发展 扩散模型实战&#xff08;三&#xff09;&#xff1a;扩散模型的应用 本文以MNIST数据集为例&#xff0c;从…

智能楼宇综合布线实训室建设方案

一、楼宇智能综合布线实训室方案概述 楼宇智能综合布线实训室方案旨在为学生提供一个真实的学习和实践环境&#xff0c;以培养他们在楼宇智能综合布线领域的实际操作能力和技能。以下是一个概述&#xff1a; 1. 培养目标&#xff1a;培养学生在楼宇智能综合布线方面的综合能力…

Shader学习(三)(片元着色器)

1、在片元着色器处理漫反射 // Upgrade NOTE: replaced _World2Object with unity_WorldToObjectShader "Custom/specularfragement" {properties{_sp("Specular",color) (1,1,1,1)_shiness("Shiness",range(1,64)) 8}SubShader{pass {tags{&…

网络通信原理应用层(第五十一课)

1)DNS:域名解析系统,端口号TCP或UDP的53 2)域名注册网站 -新网 www.xinnet.com -万网-阿里云 www.net.cn -中国互联 hulian.top 配置通过域名访问网站(NETBASE第七课)_IHOPEDREAM的博客-CSDN博客 2、FTP 1)FTP概述 -文件传输协议 -控制连接:TCP 21 <

leetcode-413. 等差数列划分(java)

等差数列划分 leetcode-413. 等差数列划分题目描述双指针 上期经典算法 leetcode-413. 等差数列划分 难度 - 中等 原题链接 - 等差数列划分 题目描述 如果一个数列 至少有三个元素 &#xff0c;并且任意两个相邻元素之差相同&#xff0c;则称该数列为等差数列。 例如&#xff0…

JMeter接口测试数据分离驱动应用

步骤&#xff1a; 创建csv文件&#xff0c;编写接口测试用例 新建线程组——创建循环控制器&#xff08;循环次数填用例总数&#xff09; 创建CSV数据文件设置&#xff0c;设置参数。&#xff08;注意&#xff1a;是否允许带引号&#xff1f;&#xff1a;一定要设置为true&a…

深度学习实战48-【未来的专家团队】基于AutoCompany模型的自动化企业概念设计与设想

大家好,我是微学AI,今天给大家介绍一下深度学习实战48-【未来的专家团队】基于AutoCompany模型的自动化企业概念设计与设想,文本将介绍AutoCompany模型的概念设计,涵盖了AI智能公司的各个角色,并结合了GPT-4接口来实现各个角色的功能,设置中央控制器,公司运作过程会生成…

JMM内存模型之happens-before阐述

文章目录 一、happens-before的定义二、happens-before的规则1. 程序顺序规则&#xff1a;2. 监视器锁规则&#xff1a;3. volatile变量规则&#xff1a;4. 传递性&#xff1a;5. start()规则&#xff1a;6. join()规则&#xff1a; 一、happens-before的定义 如果一个操作hap…

【编程二三事】ES究竟是个啥?

在最近的项目中&#xff0c;总是或多或少接触到了搜索的能力。而在这些项目之中&#xff0c;或多或少都离不开一个中间件 - ElasticSearch。 今天忙里偷闲&#xff0c;就来好好了解下这个中间件是用来干什么的。 ES是什么? ​ ES全称ElasticSearch&#xff0c;是个基于Lucen…