square/okhttp​github.com

本文基于OkHttp4.7.1分析

同步请求示例代码

OkHttpClient client = new OkHttpClient.Builder().build();Request request = new Request.Builder().url(url).build();try {Response response = client.newCall(request).execute();response.body().string();} catch (IOException e) {e.printStackTrace();}

异步请求示例代码

OkHttpClient client = new OkHttpClient.Builder().build();Request request = new Request.Builder().url(url).build();client.newCall(request).enqueue(new Callback() {@Overridepublic void onFailure(@NotNull Call call, @NotNull IOException e) {}@Overridepublic void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {response.body().string();}});

OkHttpClient类

okhttp框架的一些全局设置,以及请求的发起统统由OkHttpClient类负责

推荐使用new OkHttpClient.Builder()构建者模式进行设置相关参数并获得对象

Request类

对于请求的参数设置,依旧是由构建者模式构造,支持为请求添加请求头,请求体,请求方法等

class Request internal constructor(@get:JvmName("url") val url: HttpUrl,@get:JvmName("method") val method: String,@get:JvmName("headers") val headers: Headers,@get:JvmName("body") val body: RequestBody?,internal val tags: Map<Class<*>, Any>
) {

RealCall类

OkHttpClient.newCall(request)

override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)

OkHttpClient类实现了Call.Factory工厂接口,newCall方法返回了Call的实现类RealCall

其内部实现了三个重要的方法

class RealCall(val client: OkHttpClient,/** The application's original request unadulterated by redirects or auth headers. */val originalRequest: Request,val forWebSocket: Boolean
) : Call {//同步请求override fun execute(): Response {...}//异步请求override fun enqueue(responseCallback: Callback) {...}//通过拦截器责任链获取请求结果
@Throws(IOException::class)internal fun getResponseWithInterceptorChain(): Response {...}
}

execute()同步请求方法

 override fun execute(): Response {//判断当前call是否请求过,如果check(true)则抛出异常synchronized(this) {check(!executed) { "Already Executed" }executed = true}
//超时逻辑开启,超时时间设置在client.callTimeoutMillis 进行设置timeout.enter()
//事件监听器的请求开始时间回调callStart()try {// 调用client的dispatcher调度器执行call(将RealCall加入同步runningSyncCalls队列)client.dispatcher.executed(this)
// 通过拦截器责任链模式进行请求和返回处理等一系类逻辑return getResponseWithInterceptorChain()} finally {//不管请求是否成功,都将call移出runningSyncCalls队列client.dispatcher.finished(this)}}

RealCall.getResponseWithInterceptorChain()方法

@Throws(IOException::class)internal fun getResponseWithInterceptorChain(): Response {// 建立一个拦截器集合val interceptors = mutableListOf<Interceptor>()interceptors += client.interceptors//client配置的拦截器interceptors += RetryAndFollowUpInterceptor(client)//重试请求拦截器interceptors += BridgeInterceptor(client.cookieJar)//interceptors += CacheInterceptor(client.cache)//缓存拦截器interceptors += ConnectInterceptor//连接拦截器if (!forWebSocket) {interceptors += client.networkInterceptors}interceptors += CallServerInterceptor(forWebSocket)//执行拦截器
//生成拦截器链val chain = RealInterceptorChain(call = this,//RealCallinterceptors = interceptors,//拦截器集合index = 0,exchange = null,request = originalRequest,//Request对象connectTimeoutMillis = client.connectTimeoutMillis,readTimeoutMillis = client.readTimeoutMillis,writeTimeoutMillis = client.writeTimeoutMillis)var calledNoMoreExchanges = falsetry {//把request传入拦截器链对象的proceed方法得到responseval response = chain.proceed(originalRequest)
//取消请求call.cancel()if (isCanceled()) {response.closeQuietly()throw IOException("Canceled")}
//返回请求结果return response} catch (e: IOException) {calledNoMoreExchanges = truethrow noMoreExchanges(e) as Throwable} finally {if (!calledNoMoreExchanges) {noMoreExchanges(null)}}}
.....

配置拦截器集合,构造RealInterceptorChain对象,调用对象的proceed(Request)方法进行请求拿到请求结果Response

RealInterceptorChain类

class RealInterceptorChain(internal val call: RealCall,private val interceptors: List<Interceptor>,//拦截器链表private val index: Int,//计数器internal val exchange: Exchange?,internal val request: Request,internal val connectTimeoutMillis: Int,internal val readTimeoutMillis: Int,internal val writeTimeoutMillis: Int
) : Interceptor.Chain {@Throws(IOException::class)override fun proceed(request: Request): Response {check(index < interceptors.size)calls++if (exchange != null) {check(exchange.finder.sameHostAndPort(request.url)) {"network interceptor ${interceptors[index - 1]} must retain the same host and port"}check(calls == 1) {"network interceptor ${interceptors[index - 1]} must call proceed() exactly once"}}
//创建一个新的RealInterceptorChain实例,并将计数器index+1
//这是kotlin写法,并没有改变当前实例的计数器index值val next = copy(index = index + 1, request = request)
//取出当前顺序的拦截器,第一个index=0val interceptor = interceptors[index]
//调用拦截器的拦截方法interceptor.intercept(next)并传入新的RealInterceptorChain实例@Suppress("USELESS_ELVIS")val response = interceptor.intercept(next) ?: throw NullPointerException("interceptor $interceptor returned null")if (exchange != null) {check(index + 1 >= interceptors.size || next.calls == 1) {"network interceptor $interceptor must call proceed() exactly once"}}check(response.body != null) { "interceptor $interceptor returned a response with no body" }
//返回请求结果return response}

RealInterceptorChain.proceed()方法首先调用copy()方法建立一个自身的新的实例next

取出interceptors拦截器链表中的第一个拦截器interceptor并调用intercept(next)方法传入next

在interceptor.intercept(next)方法中会执行拦截器自己的逻辑,并调用传入的next.proceed()方法获取response,至此完成一次链式调用,然后依次调用,直至拦截器链表中的每一个拦截器都调用完毕,并倒序返回response拿到结果

至此,同步请求的流程结束,其中一些细节,大家可以自行研究

异步请求

RealCall.enquene(Callback)方法

class RealCall(override fun enqueue(responseCallback: Callback) {synchronized(this) {check(!executed) { "Already Executed" }executed = true}callStart()
//调用了dispatcher调度器的enqueue()方法client.dispatcher.enqueue(AsyncCall(responseCallback))}

查看源码可以发现RealCall.enquene(Callback)方法实际是调用了dispatcher调度器的enqueue()方法,同时传入了RealCall的内部类AsyncCall,并将Callback传入了AsyncCall的构造方法

我们先稍等分析AsyncCall类,先查看dispatcher.enqueue()方法和promoteAndExecute()方法

class Dispatcher constructor() {// 默认最大请求数 64@get:Synchronized var maxRequests = 64// 默认最大并发Host 5@get:Synchronized var maxRequestsPerHost = 5// 线程池执行器 默认创建可缓存线程池 @get:JvmName("executorService") val executorService: ExecutorServiceget() {if (executorServiceOrNull == null) {executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,SynchronousQueue(), threadFactory("OkHttp Dispatcher", false))}return executorServiceOrNull!!}//准备阶段的异步call队列private val readyAsyncCalls = ArrayDeque<AsyncCall>()//运行中的异步call队列private val runningAsyncCalls = ArrayDeque<AsyncCall>()//运行中的同步call队列private val runningSyncCalls = ArrayDeque<RealCall>()internal fun enqueue(call: AsyncCall) {synchronized(this) {//将asyncCall加入准备阶段的异步call队列readyAsyncCalls.add(call)// Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to// the same host.if (!call.call.forWebSocket) {val existingCall = findExistingCallWithHost(call.host)if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)}}
// 准备线程池执行器,及执行promoteAndExecute()}private fun promoteAndExecute(): Boolean {this.assertThreadDoesntHoldLock()
//局部变量队列缓存val executableCalls = mutableListOf<AsyncCall>()val isRunning: Booleansynchronized(this) {//获取准备阶段的异步call队列的迭代器,遍历获取队列中的异步callval i = readyAsyncCalls.iterator()while (i.hasNext()) {val asyncCall = i.next()if (runningAsyncCalls.size >= this.maxRequests) break // 已达请求数上限if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // 已达并发数上限i.remove()asyncCall.callsPerHost.incrementAndGet()executableCalls.add(asyncCall)//加入此次执行队列缓存runningAsyncCalls.add(asyncCall)// 加入正在执行队列}isRunning = runningCallsCount() > 0}for (i in 0 until executableCalls.size) {val asyncCall = executableCalls[i]
// 将线程执行器传入call,在Call中进行执行asyncCall.executeOn(executorService)}return isRunning}

dispatcher.enqueue()和dispatcher.promoteAndExecute()的主要工作就是将传入AsyncCall放入readyAsyncCalls准备队列,经过判断请求数和并发数后再遍历队列将AsyncCall放入runningAsyncCalls运行队列,并将调用asyncCall.executeOn(executorService)传入线程池

AsyncCall类

internal inner class AsyncCall(private val responseCallback: Callback) : Runnable {fun executeOn(executorService: ExecutorService) {client.dispatcher.assertThreadDoesntHoldLock()var success = falsetry {//AsyncCall实现了runnable接口,所以使用线程池执行AsyncCallexecutorService.execute(this)success = true} catch (e: RejectedExecutionException) {val ioException = InterruptedIOException("executor rejected")ioException.initCause(e)noMoreExchanges(ioException)responseCallback.onFailure(this@RealCall, ioException)} finally {if (!success) {client.dispatcher.finished(this) // This call is no longer running!}}}override fun run() {threadName("OkHttp ${redactedUrl()}") {var signalledCallback = falsetimeout.enter()try {//这里又到了之前分析的拦截器责任链请求val response = getResponseWithInterceptorChain()signalledCallback = true
//最终将结果放入callback.onResponse()返回responseCallback.onResponse(this@RealCall, response)} catch (e: IOException) {if (signalledCallback) {// Do not signal the callback twice!Platform.get().log("Callback failure for ${toLoggableString()}", Platform.INFO, e)} else {responseCallback.onFailure(this@RealCall, e)}} catch (t: Throwable) {cancel()if (!signalledCallback) {val canceledException = IOException("canceled due to $t")canceledException.addSuppressed(t)responseCallback.onFailure(this@RealCall, canceledException)}throw t} finally {client.dispatcher.finished(this)}}}}

AsyncCall类实现了Runnable接口在executeOn()方法中直接将自身放入线程池中执行

executorService.execute(this)

在run()方法中同样调用了getResponseWithInterceptorChain()方法,并将结果放入callback.onResponse()返回

至此,请求流程分析完毕

修改拦截器里的请求头_OkHttp4 源码分析(1) 请求流程分析相关推荐

  1. axios拦截器里终止请求

    1.首先我们用一个值用来保存axios的终止函数,放在vuex里,方便全局调用. const state = {cancelAxios: null // 终止axios请求 } const mutat ...

  2. 在拦截器里放入参数 controller_程序员云旅游:10分钟带你走完SpringMVC里一次HTTP请求处理之路...

    代码写累了?工作没激情?还不来一场说走就走的旅行! 今天就由本大佬(请原谅我使用了略微夸张的修辞手法)亲自带队,来为大家导游,带领大家探秘神奇的SpringMVC世界,重走一次HTTP请求处理之路,本 ...

  3. Squid 代理服务器 编译源码 伪造HTTP_X_FORWARDED_FOR 请求头

    本实验操作系统选用 CentOS release 5.6 (Final) 实验目的实现 Squid 代理服务器 编译源码 伪造HTTP_X_FORWARDED_FOR  请求头 .使其显示任意IP 过 ...

  4. python 请求头_Python爬虫:将headers请求头字符串转为字典

    第一种方法 如上图所示,然后复制粘贴到pycharm里,存放到字典里 然后使用ctrl+r快捷键打开替换功能,并勾选Regex 替换源为:(.?): (.?)$ 替换为:"$1": ...

  5. JAVA拦截器(Interceptor)实现以及原码示例

    JAVA拦截器(Interceptor)实现以及原码示例 概念 SpringMVC中拦截器[interceptor] 一.实现拦截器代码 二.单/多 个拦截器运行 1.单个拦截器 2.多个拦截器 源码 ...

  6. 爬虫实战学习笔记_6 网络请求request模块:基本请求方式+设置请求头+获取cookies+模拟登陆+会话请求+验证请求+上传文件+超时异常

    1 requests requests是Python中实现HTTP请求的一种方式,requests是第三方模块,该模块在实现HTTP请求时要比urlib.urllib3模块简化很多,操作更加人性化. ...

  7. 第二篇:白话tornado源码之待请求阶段

    上篇<白话tornado源码之一个脚本引发的血案>用上帝视角多整个框架做了一个概述,同时也看清了web框架的的本质,下面我们从tornado程序的起始来分析其源码. 概述 上图是torna ...

  8. Spring源码解析 -- SpringWeb请求参数获取解析

    Spring源码解析 – SpringWeb请求参数获取解析 简介 在文章:Spring Web 请求初探中,我们看到最后方法反射调用的相关代码,本篇文章就探索其中的参数是如何从请求中获取的 概览 方 ...

  9. Java 蜡烛图_ta-lib 里的蜡烛图形态函数源码

    ta-lib 里的蜡烛图形态函数源码 以CDL2CROWS为例, 看一看c语言的源码: 有关的源码文件包括 d:\Documents\Pictures\ta-lib\c\src\ta_func\ta_ ...

最新文章

  1. java拍照搜题软件下载_修改版|学生福利!!免费拍照搜题秒出答案,扫一扫作业出答案!...
  2. Ruby之父:写Ruby时工作特别闲,总加班的人很难做出创造
  3. python 技术篇-时间戳的获取,记录程序处理时间
  4. Boost:顺序一致性的测试程序
  5. 贪心算法-03哈夫曼编码问题
  6. 计算机蠕虫是一个程序或程序系列,它采取截取口令并试图在系统中,计算机蠕虫病毒是一个程序或程序系列,它采取截取口令并试图在系统中做非法动作的方式直接攻击计算机。...
  7. LINQ 学习路程 -- 查询语法 LINQ Query Syntax
  8. windows中端口号被占用的解决方法
  9. vue中watch的用法总结以及报错处理Error in callback for watcher checkList
  10. 如何查找cvpr类的论文_美国凯泽大学工商管理硕士:MBA论文案例编写类如何写?...
  11. Swift中的Sequence基本的使用
  12. 如何抓取http请求/拦截器用法
  13. 买房税费大攻略!哪些费用必须交?
  14. android 键盘挡住popupwindow,软键盘遮挡住popupWindow问题
  15. sqlserver加密隐私字段(不侵入程序)-Always Encrypted
  16. 如何给拍好的短视频配音?最简单的方法推荐!
  17. Vue v-for 时,单个元素class的控制
  18. docker创建的activemq配置nio不能映射端口61618
  19. 计算机多媒体在教育应用中的优点,计算机多媒体教学的利与弊论文
  20. Java实现数组列项相加_裂项求和法 - osc_rkun22vq的个人空间 - OSCHINA - 中文开源技术交流社区...

热门文章

  1. 图像运动模糊原理及python实现
  2. C++ STL inner_product函数的使用方法
  3. python制作一个桌面小工具
  4. ubuntu 远程 搭建 Jupyter Notebook 服务器配置
  5. 图像处理六:预处理方法
  6. javaScript中的 || 和 所遵循的短路现象
  7. Anacoda 介绍、安装、环境切换
  8. hibernate之6.one2many单向
  9. Paypal Rest Api自定义物流地址(跳过填写物流地址)
  10. 网站建设软件—***系统(DianCMS)1.0 发布