OkHttp完全解读

一,概述

OkHttp作为android非常流行的网络框架,笔者认为有必要剖析此框架实现原理,抽取并理解此框架优秀的设计模式。OkHttp有几个重要的作用,如桥接、缓存、连接复用等,本文笔者将从使用出发,解读源码,剖析此功能的实现原理。最后阅读完源码后总结出如下结论,OkHttp是一款优秀的网络请求框架,内部采用优雅的责任链模式、构造模式、桥接模式、享元模式、门面模式等设计模式,符合依赖导致原则、里氏替换原则等面向对象原则,将复杂的网络请求封装从简单的调用,属实优雅。笔者推荐感兴趣的读者从笔者粗陋的源码解读思路去思考更多的源码设计与实现,彻底完全了解OkHttp的设计思路,并抽象出时序图和类图。

以下是笔者粗陋的时序图总结,供读者参考。

二,主要成员解读

1,OkHpptClient

如上图,我们从OkHttp提供的API出发。

OkHttpClient,从名字可知,网络请求的客户端,该客户端主要的作用是什么呢?不妨通过其Builder的重要成员进行分析,

dispatch分发器,主要负责网络请求队列的调度,稍后再谈。

connectionPool,连接池。

interceptors,拦截器-责任链,OkHttp内置了几个重要的拦截器,有失败-重定向、桥接、缓存、连接、访问服务等。

剩下的成员读者可自行分析。那么OkHttpCient笔者认为是一系列网络请求的基础配置。

2,Call

Call是一个接口,继承了克隆接口,我们看下定义的方法,

request,返回Request,

execute,同步执行此处请求,

enqueue,异步执行此次请求,

cancel,取消此次请求,

isExecuted,是否执行完毕,

isCanceld,是否成功取消,

timeout,返回超时相关

因此,Call可以理解为一次Request,通过OkHttpClient.newCall创建,其唯一实现是RealCall,

构造函数中需传入OkHttpClient,原始请求Request,是否WebSocket属性。

3,Request

Request不用笔者多说,主要封装request,包裹了url、method、headers、body等http基础request字段。

4,Response

封装了message,code,hearer,body等。

以上Request和Response是网络请求的主要实体类。

5,Dispatch

笔者认为这是异步请求时主要的调度器,调度对象是Call,内部定义有如下三种队列,

raedyAsyncCalls是通过Call#enqueue添加到此的Call队列,

runningAsyncCalls,从readyAsyncCalls中转移到此队列,表现正在运行的异步Call,

runningSyncCalls,同步运行队列,

下面我们看下调度方法promoteAndExecute

主要在enqueue、finished时调用,作用是将符合条件的raedyAsyncCalls转移至runningAsyncCalls中。

在从readyAsyncCalls转移到runningAsyncCalls时,有默认最大运行数maxRequests64。将真正执行运行的Call放进executeableCalls,然后碟调调用其executeOn方法,传入的executorService是一个线程池,

核心线程数定义0,最大容量MAX_VALUE,因maxRequests的存在,可以忽略此参数。

6,Interceptor&Chin

Interceptor只定义了一个方法Intercept,参数为Chain,每个拦截器负责处理自己的逻辑,如果处理完毕并且需要下一个拦截器处理,需要显式调用Chain#proceed方法。

我们看下Chain接口的唯一实现RealInterceptorChain,

传入此次请求call,拦截器,拦截器执行下标index0,原始请求request等,主要供多个拦截器从Chain中获取信息,我们看下核心方法process,

Interceptor每调用一次proceed方法,会触发Chain被负责,传入index+1(这使得同一个拦截器可以多次调用proceed方法,从该节点重试),进一步获取到下一个拦截器,再调用其拦截器intercept方法。这样就实现了链式请求。

以上,我们介绍了OkHttp框架的主要角色,下面介绍下一次请求的主要流程,以及各个重要拦截器所做的工作。

三,一次请求解读

1,伊始

笔者还得从创建了一个Call对象开始,

我们跟进enqueue方法

原子式设置executed为true,否则抛出异常,合理,一个call只能调用enqueue一次。

client是通过RealCall构造方法传入,我们进入Dispatcher#enqueue查看。笔者注意,这里的RealCall在进入dispatcher时,被转换成了AsyncCall,这个稍后再谈。

所做的事情,是将异步call放入readAsyncCalls中,如果不是webSocket需做点什么,这个笔者不展开说,主要调用到调度方法promoteAndExecute。

这个方法如在Dispatch中解读,加入runningAsyncCalls,并且加入到executableCalls列表,调用AsyncCall#executeOn方法,我们跟进。

AsyncCall实现了Running接口,并且封装了RealCall,我们直接看run方法的实现,

通过TimeOut#enter开启请求超时调度(如果设置的话),然后最重要的调用了getResponseWithInterceptorChain方法,直接返回请求到的Response。当请求完毕后,最终调用到dispatcher.finished方法,我们暂且先查看finished方法,

将执行完毕的Call从runingAsyncCalls中移除,然后在调用一次promoteAndExecute方法,将准备队列的Call执行,如果没有Call执行了,就调用闲置回调idleCallback。这样就实现了队列的简单调度。那么,我们将注意力重新回到核心方法getResponseWithInterceptorChain。

创建一个拦截器list,先放入OkHttpClient中用户自定义的拦截器,随后放入几个核心拦截器,

RetryAndFollowUpInterceptor、负责重试重定向的拦截器。

BridgeInterceptor、桥接拦截器,负责自动设置一些heads、cook等。

CacheInterceptor、缓存的核心实现拦截器。

ConnectInterceptor、连接拦截器,维护了一个连接池,复用连接核心逻辑拦截器。

CallServerInterceptor、与服务器正式请求的拦截器,

这些全部封装进RealInterceptorChain方法中,然后调用proceed方法,参数是request,

通过上文我们了解proceed是顺序调用下一个拦截器逻辑,因此,笔者这里暂忽略用户自定义拦截器,直接顺序解读核心拦截器实现逻辑。

2,RetryAndFollowUpInterceptor

一个无限循环,直接调用chain.process让下一个拦截器处理,然后解析Response,

通过followUpRequest解析Response,如果返回空,代表无需重试或重定向,直接返回Response。否则,重复调用chain#proceed(注意,chain在realChanin中通过copy方法实现原型模式,因此后面的index+1对此处无影响,chain#index仍为原始值)

我们看下followUpRequest方法,

对各种Response#code作解析,新创建Request,当Response正常返回,此方法返回null,笔者在此处不展开说,有兴趣的读者可自行研究。接下来,我们看下一个拦截器。

3,BridgeInterceptor

此处,将OkHttpClient的cookieJar保存,继续跟进拦截逻辑,

(1)如果存在body,body中存在contentType,自动设置进Request的Hearer中,

(2)如果存在body,且内容长度不等于-1,自动设置Content-Length头,移除Transfer-Encoding头。否则,移除Content-Length头,添加Transfer-Encoding头。感兴趣的读者可以主动去了解下这些请求头的意思。

(3)如果Request的Hearer中Host为空,则从请求url中设置host。

(4)如果没有设置Connection字段,自动设置“Keep-Alive”,意保持连接。

(5)继续添加请求头,Cookie、User-Aagent,

笔者在这里解释,桥接拦截器的作用就是自动设置一些请求头,减少客户端操作复杂度。

接下来,就是对Response作解析,如cookieJar解析、Response#解码相关,感兴趣的读者自行了解,笔者在此不展开讲。

4,CacheInterceptor

实现缓存相关,核心逻辑如下,

根据Request从cache中获取Response,随后获取Request的缓存策略

如果从缓存中获取到Response,但是cacheResponse为null,代表此次请求不适用缓存,调用closeQuitely关闭缓存。

如果不能使用网络,且无缓存,返回失败Response。

如果不访问网络请求,那就直接从缓存中获取并返回,就不走接下来的拦截器了。

如果可以访问网络,但策略是访问网络,调用listenr#cacheConditionlHit回调,通知观察者缓存命中或缓存没命中。

于是,请求到下一个拦截器,当返回Response时,

Response code 返货 not modified,调用cache方法更新缓存

如果Response有效,添加到缓存中,另外笔者注意到,如果method不支持缓存,则移除,我们看下哪些不支持呢?

POST/DELETE/PATCH/PUT/MOVE是不支持缓存的,而GET/HEAD...才支持缓存。

5,ConnectInterceptor

通过获取到exchange,调用realChain#copy方法将exchange传入,作为一次连接复用。我们跟进看看initExchange方法,

通过exchangeFinder#find方法复用ExchangeCodec,笔者猜测这是实现连接的主要核心类,我们现看下ExchangeCodec是什么。ExchangeCodec是一个接口,方法定义如下,

从方法中,笔者猜测到这代表了连接实体,通过flushRequest发送给服务器请求,我们看看这个接口的实现类

笔者这里看下Http1ExchangeCodec,有兴趣的读者可以自己去看Http2ExchangeCodec,

从成员中发现RealConnection,因此这封装了一次连接;

BufferedSink,从命名知这是连接打开的缓冲区,通过flushReques将缓存区的数据发送给服务端。具体如何发送给服务器笔者暂不跟进。

先回到复用逻辑,exchangeFinder#find方法,

(1),首先从连接池中获取连接,如果无法获取,则新建连接,

调用RealCall#acquireConnectionNoEvents方法,将复用逻辑设置进connect成员中。

(2)如果没有复用连接,则新创建连接,

我们跟进connect,笔者直接快进到connectSockt,

当连接成功后,获取到source和sink,前者作为接受Response的缓存,后者就是发送缓存,

关于如何发送,上文已经介绍。那么如何接收呢?如下,在Http1ExchangeCodec#AbstractSource中。

跟进到底层有如下代码,笔者在此不再跟进,后面是基本的一些读取相关。有兴趣读者自行了解。

在这个拦截器中,笔者注意到创建或者复用了Connect,接下来,就是通过连接访问服务器了。

6,CallServerInterceptor

通过exchange写入request中的Hearer到缓存区中,

写入请求体什么的,到缓存区中。

最后,调用exchange,finishRequest,通过sink,flush,发送数据并清空缓存区。

随后,通过readResponseHearer,获取Response请求头,

获取请求头完毕后,接下来去读取请求body,

随后赋值给Response,即完成了一次请求。通过将Response链式返回给Chain,最后在AsyncCall中调用onResponse方法,即可通知客户端请求完成。

于是乎,一次请求的过程解读完毕,感谢读者的耐心观看。

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

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

相关文章

深度视频恢复软件推荐,轻松恢复视频文件!

“我在电脑上保存了一些视频,但在清理时却不小心将这些视频删除了,有什么方法可以恢复删除的视频吗?希望大家给我推荐一些好用的方法。” 随着科技的飞速发展,数字媒体已经成为了我们生活中不可或缺的一部分。然而,数字…

国图公考:考公和考编一样吗?

公务员:是指在各级机关中,符合规定,行使职权,执行公务的人员 事业单位:事业单位是指由国家或社会组织举办,从事教育、科学、文化、卫生、体育等社会公益事业的单位。 公务员和事业编都是有编制的&#xf…

dataframe 列按指定字符截取

创建一个示例 import pandas as pd data {Column1: [1~2, 21~3, 3~41, 411~5], } test_df pd.DataFrame(data) print(test_df) 截取 ’~ ‘前、后的值 test_df[Column1_left] test_df[Column1].apply(lambda x: x.split(~)[0] if pd.notnull(x) else np.nan) test_df[…

某赛通电子文档安全管理系统 PolicyAjax SQL注入漏洞复现

0x01 产品简介 某赛通电子文档安全管理系统(简称:CDG)是一款电子文档安全加密软件,该系统利用驱动层透明加密技术,通过对电子文档的加密保护,防止内部员工泄密和外部人员非法窃取企业核心重要数据资产,对电子文档进行全生命周期防护,系统具有透明加密、主动加密、智能…

推荐系统|排序_MMOE

MMOE MMOE是指Multi-gate Mixture-of-Experts 注意看Expert后面加了s,说明了有多个专家。 而在MMOE中专家是指用来对输入特征计算的神经网络,每个神经网络根据输入计算出来的向量都会有所不同。 MMOE的低层 MMOE的上一层 通过MMOE的低层算出的向量和权…

Markdown 图片尺寸对齐等详细使用

✍️作者简介:小北编程(专注于HarmonyOS、Android、Java、Web、TCP/IP等技术方向) 🐳博客主页: 开源中国、稀土掘金、51cto博客、博客园、知乎、简书、慕课网、CSDN 🔔如果文章对您些帮助请👉关…

全链路压测的关键点是什么?

全链路压测是一种重要的性能测试方法,用于评估应用程序或系统在真实生产环境下的性能表现。通过模拟真实用户行为和流量,全链路压测能够全面评估系统在不同负载下的稳定性和性能表现。本文将介绍全链路压测的关键点,以帮助企业更好地理解和应…

【第二十二课】最短路:dijkstra算法 ( acwing849 / acwing850 / c++ 代码)

目录 dijkstra算法求最短距离步骤 朴素的dijkstra算法---acwing-849 代码如下 代码思路 堆优化版的dijkstra算法---acwing-850 代码如下 关于最短路问题分有好几种类型 : 单源就是指:只求从一个顶点到其他各顶点 多源是指:要求每个顶…

SD-WAN和MPLS的区别以及如何选择?

网络连接技术的选择对企业来说至关重要。SD-WAN(软件定义广域网)和MPLS(多协议标签交换)是两种备受关注的网络连接方案。它们在架构、带宽、成本和管理等方面存在显著区别,企业应了解清楚这些区别再进行选择。 SD-WAN采…

AI算力专题:从超微电脑创新高看AI算力产业链高景气

今天分享的是AI算力系列深度研究报告:《AI算力专题:从超微电脑创新高看AI算力产业链高景气》。 (报告出品方:太平洋证券) 报告共计:10页 海外巨头指引 Al 算力产业链高景气 超微电脑业绩指引大幅上调反映…

三子棋游戏小课堂

🪐🪐🪐欢迎来到程序员餐厅💫💫💫 今天的主菜是,C语言实现的三子棋小游戏, 所属专栏: C语言知识点 主厨的主页:Chef‘s blog 前言&…

机器学习 | 掌握逻辑回归在实践中的应用

目录 初识逻辑回归 逻辑回归实操 分类评估方法 初识逻辑回归 逻辑回归(LogisticRegression)是机器学习中的一种分类模型,逻辑回归是一种分类算法,虽然名字中带有回归,但是它与回归之间有一定的联系。由于算法的简单…

【Spark系列2】Spark编程模型RDD

RDD概述 RDD最初的概述来源于一片论文-伯克利实验室的Resilient Distributed Datasets:A Fault-Tolerant Abstraction for In-Memory Cluster Computing。这篇论文奠定了RDD基本功能的思想 RDD实际为Resilient Distribution Datasets的简称,意为弹性分…

【大厂AI课学习笔记】1.3 人工智能产业发展(2)

(注:腾讯AI课学习笔记。) 1.3.1 需求侧 转型需求:人口红利转化为创新红利。 场景丰富:超大规模且多样的应用场景。主要是我们的场景大,数据资源丰富。 抗疫加速:疫情常态化,催生新…

Windows11通过Hyper-V创建VM,然后通过vscode连接vm进行开发

这边需要在win11上建立vm来部署docker(这边不能用windows版本的docker destop),学习了下,记录。 下载系统镜像 首先下载系统镜像:https://releases.ubuntu.com/focal/ 这边使用的是ubuntu20.04.6 LTS (Focal Fossa) ,Server inst…

CIFAR-10数据集详析:使用卷积神经网络训练图像分类模型

1.数据集介绍 CIFAR-10 数据集由 10 个类的 60000 张 32x32 彩色图像组成,每类 6000 张图像。有 50000 张训练图像和 10000 张测试图像。 数据集分为5个训练批次和1个测试批次,每个批次有10000张图像。测试批次正好包含从每个类中随机选择的 1000 张图像…

易优CMS采集插件使用教程

本易优CMS采集教程说明如何使用易优CMS采集插件,批量获取互联网上的文章数据,并自动更新到易优cms(eyoucms)网站,快速丰富网站的内容。 目录 1. 下载并安装易优CMS采集插件 2. 对接网页文章采集工具 3. 采集数据发…

GPT-4级别模型惨遭泄露!引爆AI社区,“欧洲版OpenAI”下场认领

大家好,我是二狗。 这两天,一款性能接近GPT-4的模型惨遭泄露,引发了AI社区的热议。 这背后究竟是怎么回事呢? 起因是1月28日,一位名为“Miqu Dev”的用户在 HuggingFace 上发布了一组文件,这些文件共同组…

智慧工地可视化综合管理云平台 PC+APP

目录 一、智慧工地可视化数据大屏功能一览 1.首页 2.视频监控 3.机械设备 4.环境监测 5.安全管理 6.质量管理 7.劳务分析 8.进度管理 9.报警统计 二、项目人员管理 1.信息管理 2.信息采集 3.证件管理 危大工程管理 一、智慧工地可视化数据大屏功能一览 包括&am…

【C语言】const修饰指针的不同作用

目录 const修饰变量 const修饰指针变量 ①不用const修饰 ②const放在*的左边 ③const放在*的右边 ④*的左右两边都有const 结论 const修饰变量 变量是可以修改的,如果把变量的地址交给⼀个指针变量,通过指针变量的也可以修改这个变量。 但…