Kotlin 中 OkHttp 使用及解析
build.gradle
dependencies {//OkHttpimplementation 'com.squareup.okhttp3:okhttp:4.9.0'
}
简单使用例子
val okHttpClient = OkHttpClient.Builder().connectTimeout(Duration.ofSeconds(10)).readTimeout(Duration.ofSeconds(10)).writeTimeout(Duration.ofSeconds(10)).retryOnConnectionFailure(true).build()val request = Request.Builder().url(url).build()val call = okHttpClient.newCall(request)call.enqueue(object : Callback {override fun onFailure(call: Call, e: IOException) {Log.e("TAG", "onFailure:${e.message}")}override fun onResponse(call: Call, response: Response) {Log.d("TAG", "onResponse: ${response.body?.string()}")}})
1、通过 Builder 模式得到 okHttpClient ,OkHttpClient 包含了对网络请求的全局配置信息,包括链接超时时间、读写超时时间、链接失败重试等配置。
2、通过 Builder 模式得到 request ,Request 包含了本次网络请求的所有请求参数,包括 url、method、headers、body 等。
3、通过 newCall 方法得到 call,Call 就是用于发起请求,可用于执行 同步请求(execute)、异步请求(enqueue)、取消请求(cancel)等各种操作。
4、调用 enqueue 方法发起异步请求返回 response ,Response 就包含了此次网络请求的所有返回信息。
5、拿到 Response 对象的 body 并以字符串流的方式进行读取。
一、OkHttpClient
OkHttpClient 使用 Builder模式来完成初始化,其提供了很多配置参数,每个选项都有默认值。
class Builder constructor() {internal var dispatcher: Dispatcher = Dispatcher()internal var connectionPool: ConnectionPool = ConnectionPool()internal val interceptors: MutableList<Interceptor> = mutableListOf()internal val networkInterceptors: MutableList<Interceptor> = mutableListOf()internal var eventListenerFactory: EventListener.Factory = EventListener.NONE.asFactory()internal var retryOnConnectionFailure = trueinternal var authenticator: Authenticator = Authenticator.NONEinternal var followRedirects = trueinternal var followSslRedirects = trueinternal var cookieJar: CookieJar = CookieJar.NO_COOKIESinternal var cache: Cache? = nullinternal var dns: Dns = Dns.SYSTEMinternal var proxy: Proxy? = nullinternal var proxySelector: ProxySelector? = nullinternal var proxyAuthenticator: Authenticator = Authenticator.NONEinternal var socketFactory: SocketFactory = SocketFactory.getDefault()internal var sslSocketFactoryOrNull: SSLSocketFactory? = nullinternal var x509TrustManagerOrNull: X509TrustManager? = nullinternal var connectionSpecs: List<ConnectionSpec> = DEFAULT_CONNECTION_SPECSinternal var protocols: List<Protocol> = DEFAULT_PROTOCOLSinternal var hostnameVerifier: HostnameVerifier = OkHostnameVerifierinternal var certificatePinner: CertificatePinner = CertificatePinner.DEFAULTinternal var certificateChainCleaner: CertificateChainCleaner? = nullinternal var callTimeout = 0internal var connectTimeout = 10_000internal var readTimeout = 10_000internal var writeTimeout = 10_000internal var pingInterval = 0internal var minWebSocketMessageToCompress = RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZEinternal var routeDatabase: RouteDatabase? = null
}
二、Request
Request 包含了网络请求时的所有请求参数,一共包含以下五个。
open class Builder {internal var url: HttpUrl? = nullinternal var method: Stringinternal var headers: Headers.Builderinternal var body: RequestBody? = null/** A mutable map of tags, or an immutable empty map if we don't have any. */internal var tags: MutableMap<Class<*>, Any> = mutableMapOf()
}
三、Call
当调用 okHttpClient.newCall(request) 时就会得到一个 call 对象。
/** Prepares the [request] to be executed at some point in the future. */override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
call 是一个接口,我们可以将其看做是网络请求的启动器,可用于同步请求异步请求,但重复发起多次请求的话会抛出异常。
interface Call : Cloneable {/** Returns the original request that initiated this call. */fun request(): Request/*** Invokes the request immediately, and blocks until the response can be processed or is in error.** To avoid leaking resources callers should close the [Response] which in turn will close the* underlying [ResponseBody].** ```* // ensure the response (and underlying response body) is closed* try (Response response = client.newCall(request).execute()) {* ...* }* ```** The caller may read the response body with the response's [Response.body] method. To avoid* leaking resources callers must [close the response body][ResponseBody] or the response.** Note that transport-layer success (receiving a HTTP response code, headers and body) does not* necessarily indicate application-layer success: `response` may still indicate an unhappy HTTP* response code like 404 or 500.** @throws IOException if the request could not be executed due to cancellation, a connectivity* problem or timeout. Because networks can fail during an exchange, it is possible that the* remote server accepted the request before the failure.* @throws IllegalStateException when the call has already been executed.*/@Throws(IOException::class)fun execute(): Response/*** Schedules the request to be executed at some point in the future.** The [dispatcher][OkHttpClient.dispatcher] defines when the request will run: usually* immediately unless there are several other requests currently being executed.** This client will later call back `responseCallback` with either an HTTP response or a failure* exception.** @throws IllegalStateException when the call has already been executed.*/fun enqueue(responseCallback: Callback)/** Cancels the request, if possible. Requests that are already complete cannot be canceled. */fun cancel()/*** Returns true if this call has been either [executed][execute] or [enqueued][enqueue]. It is an* error to execute a call more than once.*/fun isExecuted(): Booleanfun isCanceled(): Boolean/*** Returns a timeout that spans the entire call: resolving DNS, connecting, writing the request* body, server processing, and reading the response body. If the call requires redirects or* retries all must complete within one timeout period.** Configure the client's default timeout with [OkHttpClient.Builder.callTimeout].*/fun timeout(): Timeout/*** Create a new, identical call to this one which can be enqueued or executed even if this call* has already been.*/public override fun clone(): Callfun interface Factory {fun newCall(request: Request): Call}
}
ReallCall 是 Call 接口的唯一实现类
当调用 execute 方法发起同步请求时,
1、判断是否重复请求。
2、时间记录。
3、将自身加入到 dispatcher 中,并在请求结束时从 dispatcher 中移除自身。
4、通过 getResponseWithInterceptorChain 方法得到 response 对象。
override fun execute(): Response {check(executed.compareAndSet(false, true)) { "Already Executed" }timeout.enter()callStart()try {client.dispatcher.executed(this)return getResponseWithInterceptorChain()} finally {client.dispatcher.finished(this)}}
四、Dispatcher
Dispatcher 是一个调度器,用于对全局的网络请求进行缓存调度,其包含一下几个成员变量。
var maxRequests = 64var maxRequestsPerHost = 5/** Ready async calls in the order they'll be run. */
private val readyAsyncCalls = ArrayDeque<AsyncCall>()/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private val runningAsyncCalls = ArrayDeque<AsyncCall>()/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private val runningSyncCalls = ArrayDeque<RealCall>()
1、maxRequests 同一时间允许并发执行网络请求的最大线程数。
2、maxRequestsPerHost 同一个 host 下的最大同时请求数。
3、readyAsyncCalls 保存当前等待执行的异步任务
4、runningAsyncCalls 保存当前正在执行的异步任务。
5、runningSyncCalls 保存等钱正在执行的同步任务。
五、getResponseWithInterceptorChain
其主要逻辑就是通过拦截器来完成整个网络请求过程。
@Throws(IOException::class)internal fun getResponseWithInterceptorChain(): Response {// Build a full stack of interceptors.val interceptors = mutableListOf<Interceptor>()interceptors += client.interceptorsinterceptors += RetryAndFollowUpInterceptor(client)interceptors += BridgeInterceptor(client.cookieJar)interceptors += CacheInterceptor(client.cache)interceptors += ConnectInterceptorif (!forWebSocket) {interceptors += client.networkInterceptors}interceptors += CallServerInterceptor(forWebSocket)val chain = RealInterceptorChain(call = this,interceptors = interceptors,index = 0,exchange = null,request = originalRequest,connectTimeoutMillis = client.connectTimeoutMillis,readTimeoutMillis = client.readTimeoutMillis,writeTimeoutMillis = client.writeTimeoutMillis)var calledNoMoreExchanges = falsetry {val response = chain.proceed(originalRequest)if (isCanceled()) {response.closeQuietly()throw IOException("Canceled")}return response} catch (e: IOException) {calledNoMoreExchanges = truethrow noMoreExchanges(e) as Throwable} finally {if (!calledNoMoreExchanges) {noMoreExchanges(null)}}}
六、interceptor
interceptor 多个拦截器增加串行调用逻辑
package com.gxxclass Request
class Responseinterface Chain {fun request(): Requestfun proceed(request: Request): Response
}interface Interceptor {fun intercept(chain: Chain): Response
}class RealInterceptorChain(private val request: Request,private val interceptors: List<Interceptor>,private val index: Int
) : Chain {private fun copy(index: Int): RealInterceptorChain {return RealInterceptorChain(request, interceptors, index)}override fun request(): Request {return request}override fun proceed(request: Request): Response {val next = copy(index + 1)val interceptor = interceptors[index]return interceptor.intercept(next)}
}class LogInterceptor : Interceptor {override fun intercept(chain: Chain): Response {val request = chain.request()println("LogInterceptor -- getRequest")val response = chain.proceed(request)println("LogInterceptor ---- getResponse")return response}
}class HeaderInterceptor : Interceptor {override fun intercept(chain: Chain): Response {val request = chain.request()println("HeaderInterceptor -- getRequest")val response = chain.proceed(request)println("HeaderInterceptor ---- getResponse")return response}
}class CallServerInterceptor : Interceptor {override fun intercept(chain: Chain): Response {val request = chain.request()println("CallServerInterceptor -- getRequest")val response = Response()println("CallServerInterceptor ---- getResponse")return response}
}fun main() {val interceptorList = mutableListOf<Interceptor>()interceptorList.add(LogInterceptor())interceptorList.add(HeaderInterceptor())interceptorList.add(CallServerInterceptor())val request = Request()val realInterceptorChain = RealInterceptorChain(request, interceptorList, 0)val response = realInterceptorChain.proceed(request)println("main response")
}/*
fun main() {val interceptorList = mutableListOf<Interceptor>()interceptorList.add(LogInterceptor())interceptorList.add(HeaderInterceptor())val request = Request()val realInterceptorChain = RealInterceptorChain(request, interceptorList, 0)val response = realInterceptorChain.proceed(request)println("main response")
}*/
参考:
https://github.com/leavesCZY/AndroidGuide/blob/master/%E4%B8%BB%E6%B5%81%E5%BC%80%E6%BA%90%E5%BA%93%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%EF%BC%8811%EF%BC%89OkHttp%20%E6%BA%90%E7%A0%81%E8%AF%A6%E8%A7%A3.md
相关文章:
Kotlin 中 OkHttp 使用及解析
build.gradle dependencies {//OkHttpimplementation com.squareup.okhttp3:okhttp:4.9.0 } 简单使用例子 val okHttpClient OkHttpClient.Builder().connectTimeout(Duration.ofSeconds(10)).readTimeout(Duration.ofSeconds(10)).writeTimeout(Duration.ofSeconds(10)).re…...

【C++代码】用栈实现队列,用队列实现栈--代码随想录
队列是先进先出,栈是先进后出。卡哥给了关于C方向关于栈和队列的4个问题: C中stack 是容器么? 使用的stack是属于哪个版本的STL? 使用的STL中stack是如何实现的? stack 提供迭代器来遍历stack空间么? …...

肖sir__linux详解__001
linux详解: 1、ifconfig 查看ip地址 2、6版本:防火墙的命令: service iptables status 查看防火墙状态 service iptables statrt 开启防火墙 service iptables stop 关闭防火墙 service iptables restart 重启防火墙状态 7版本: systemctl s…...

【Android Framework系列】第12章 RecycleView相关原理及四级缓存策略分析
1 RecyclerView简介 RecyclerView是一款非常强大的widget,它可以帮助您灵活地显示列表数据。当我开始学习 RecyclerView的时候,我发现对于复杂的列表界面有很多资源可以参考,但是对于简单的列表展现就鲜有可参考的资源了。虽然RecyclerView的…...

P1886 滑动窗口 /【模板】(双端队列)+双端队列用法
例题 有一个长为 n 的序列 a,以及一个大小为 k 的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。 例如: The array is [1,3,−1,−3,5,3,6,7],and k3。 输入格式 输入一共有两行…...

网络渗透day6-面试01
😉 和渗透测试相关的面试问题。 介绍 如果您想自学网络渗透,有许多在线平台和资源可以帮助您获得相关的知识和技能。以下是一些受欢迎的自学网络渗透的平台和资源: Hack The Box: Hack The Box(HTB)是一个受欢迎的平…...
Docker 及 Docker Compose 安装指南
Docker 是一个开源的容器化平台,可以帮助我们快速构建、打包和运行应用程序。而 Docker Compose 则是用于管理多个容器应用的工具,可以轻松定义和管理多个容器之间的关系。现在,让我们开始安装过程吧! docker 安装 apt安装 sudo…...

Gitlab创建一个空项目
1. 创建项目 Project slug是访问地址的后缀,跟前边的ProjectUrl拼在一起,就是此项目的首页地址; Visibility Level选择默认私有即可,选择内部或者公开,就会暴露代码。 勾选Readme选项,这样项目内默认会带…...

C语言-内存分布(STM32内存分析)
C/C内存分布 一、内存组成二、静态区域文本段 (Text / 只读区域 RO)已初始化读写数据段(RW data -- Initialized Data Segment)未初始化数据段(BSS -- Block Started by Symbol) 三、动态区域堆(…...
Linux上配置NAT
Linux系统上实现NAT上网是一个挑战性的任务,需要对操作系统进行合理的配置。本文将概述在Linux上实现NAT上网,并给出相应的工作步骤。 NAT,即Network Address Translation,是一种网络部署技术,可以在peivate network&…...
springboot实现简单的消息对话
目录 一、前言 二、实战步骤 步骤 1: 步骤 2: 步骤 3: 步骤 4: 一、前言 要在Spring Boot项目中实现消息对话,你可以使用WebSocket技术。WebSocket是一种在客户端和服务器之间提供实时双向通信的协议。 二、实…...
「Tech初见」Linux驱动之blkdev
目录 一、Motivation二、SolutionS1 - 块设备驱动框架(1)注册块设备(2)注销块设备(3)申请 gendisk(4)删除 gendisk(5)将 gendisk 加入 kernel(6&a…...
ssh配置(二、登录服务器)
一. 登录 linux 服务器的两种方式 使用 ssh用户名密码 的方式登录,但这种方式不安全,密码太简单容易被暴力破解,密码太复杂又不容易记。使用 ssh公私钥 的方式登录。 以上两种方式都可以在图形化软件工具中配置,例如 finalshell…...

pytorch异常——RuntimeError:Given groups=1, weight of size..., expected of...
文章目录 省流异常报错异常截图异常代码原因解释修正代码执行结果 省流 nn.Conv2d 需要的输入张量格式为 (batch_size, channels, height, width),但您的示例输入张量 x 是 (batch_size, height, width, channels)。因此,需要对输入张量进行转置。 注意…...

【FPGA项目】沙盘演练——基础版报文收发
第1个虚拟项目 前言 点灯开启了我们的FPGA之路,那么我们来继续沙盘演练。 用一个虚拟项目,来入门练习,以此步入数字逻辑的…...

【C++技能树】继承概念与解析
Halo,这里是Ppeua。平时主要更新C,数据结构算法,Linux与ROS…感兴趣就关注我bua! 继承 0. 继承概念0.1 继承访问限定符 1. 基类和派生类对象赋值兼容转换2. 继承中的作用域3. 派生类中的默认成员函数4.友元5.继承中的静态成员6.菱…...

计算机网络 第二节
目录 一,计算机网络的分类 1.按照覆盖范围分 2.按照所属用途分 二,计算机网络逻辑组成部分 1.核心部分 (通信子网) 1.1电路交换 1.2 分组交换 两种方式的特点 重点 2.边缘部分 (资源子网) 进程通信的方…...

无涯教程-机器学习 - 矩阵图函数
相关性是有关两个变量之间变化的指示,在前面的章节中,无涯教程讨论了Pearson的相关系数以及相关的重要性,可以绘制相关矩阵以显示哪个变量相对于另一个变量具有较高或较低的相关性。 在以下示例中,Python脚本将为Pima印度糖尿病数…...
Redis 高可用与集群
Redis 高可用与集群 虽然 Redis 可以实现单机的数据持久化,但无论是 RDB 也好或者 AOF 也好,都解决 不了单点宕机问题,即一旦单台 redis 服务器本身出现系统故障、硬件故障等问题后, 就会直接造成数据的丢失,因此需要…...
修改文件名后Git仓上面并没有修改
场景: 我在本地将文件夹名称由Group → group ,执行git push 后,远程分支上的文件名称并没有修改。 原因: 是我绕过了git 直接使用了系统的重命名操作。 在 Git 中,对于已经存在的文件或文件夹进行大小写重命名是一个敏感的操作…...

智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
重启Eureka集群中的节点,对已经注册的服务有什么影响
先看答案,如果正确地操作,重启Eureka集群中的节点,对已经注册的服务影响非常小,甚至可以做到无感知。 但如果操作不当,可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...