欢迎来到Introzo百科
Introzo百科
当前位置:网站首页 > 技术 > 从http协议角度分析okhttp

从http协议角度分析okhttp

日期:2023-10-01 09:17

-->

Okhttp简介

OkHttp是Square公司开发的开源网络框架,封装了高性能的http请求库。

  • 支持spdy、http2.0、websocket等协议
  • 支持同步和异步请求
  • 封装线程池和数据转换,提高性能。
  • Android 6.0自带的网络请求API底层是使用okhttp
  • 实现的
  • 使用更接近真实HTTP协议框架的okhttp

其他优点请参见:Android网络框架对比(稍后更新)

说到okhttp的介绍,就介绍这几个关键类吧!

Okhttp中几个重要的类介绍

OkHttpClient

该类主要用于配置okhttp框架。通俗地说,这个类管理这个框架的各种设置。

Call类工厂,可以通过OkHttpClient获取Call对象。

OkHttpClient使用注意事项

OkHttpClient 应该共享。使用okhttp这个框架时,最好将OkHttpClient设置为单例模式。所有的HTTP请求都是要使用这个Client。因为每个OkHttpClient都对应着自己的连接池和线程池。减少连接池和线程池的使用可以减少延迟和内存使用。相反,如果每个请求都创建一个OkHttpClient,就会造成内存资源的浪费。

创建OkHttpClient

OkHttpClient共有三种创建方法

第一种方法:只需使用new OkHttpClient()创建实例对象即可。该实例对象具有默认配置。默认请求连接超时时间为10秒,读写超时时间为10秒。如果连接不成功,会自动重新连接。

第二种方法是通过Builder定义一个OkHttpclient。当然,如果你直接build而不自己配置参数的话,效果会和第一种方法一样。

public 最终 OkHttpClient = new OkHttpClient.Builder()
.addInterceptor(new HttpLoggingInterceptor())
.cache(新缓存(cacheDir,cacheSize))
.等待配置
.build();

第三种方法:使用已有的OkHttpClient对象复制一个共享线程池等资源的OkHttpClient对象。

OkHttpClient agerClient = client.newBuilder()
.readTimeout(500,TimeUnit.MILLSECONS)
.build();

这种方法的好处是,当我们有特殊要求,有些配置有点不同的时候,比如连接需要超过1s,就会超时。这时候我们就可以使用这个方法来生成一个新的实例对象。然而,他们共享许多其他资源,不会浪费资源。

OkHttpClient的配置变更全部在Builder中进行

如果不再需要可以关闭它

事实上,持有的线程池和连接池如果空闲的话会自动释放。

也可以自动释放。释放后,以后调用将被拒绝。

client.dispatcher().excurorService().shutdown()

清除连接池。注意,清除后,连接池的守护线程可能会立即退出。

client.connectionPool().evictAll()

如果客户端有缓存,可以关闭。注意:再次调用关闭的缓存会导致错误。它还会导致崩溃。

client.cache().close();

OkHttp 在连接到 HTTP/2 时也使用守护线程。空闲时它们会自动退出。

只要知道有这么个东西,一般都不会主动去调用。

拨打班级

Call 该类是用于发送HTTP请求和读取HTTP响应的类

这个类的方法很少。从上到下分别是:放弃请求、异步执行请求、同步执行请求。

请求课程

该类相当于httprequest中的请求消息,用于表达请求消息,所以这里可以设置请求url、请求头、请求体等以及请求消息相关内容。

主要方法列举:

//获取请求url
公共 HttpUrl url();
// 获取请求方法类型
公共字符串方法();
// 获取请求头
公共标头 headers();
//获取请求体
public RequestBody body();
// 获取标签
公共对象标签();
// 返回缓存控制指令,即使响应不包含 Cache-Control 响应标头,也绝不为 null
公共 CacheControl cacheControl();
// 是否是https请求
public boolean isHttps();
// 请求{method=" ",url=" ",tag = " "}
公共字符串 toString();

这是其Builder中提供的方法。仅设置.url()时,默认为post请求。

请求正文

介绍完请求消息,就该介绍请求体了。这与http协议密切相关。

RequestBody 用于设置请求体。其主要方法是以下静态方法来生成相应的请求体:

就是通过这些方法来生成对应的不同请求体。 MediaType用于描述请求体或响应体类型。例如,请求体类型为字符串格式的json,则对应的MediaType为 MediaType.parse("application/json; charset=utf-8");,如果上传文件,然后相应的一个是application/octet-stream,并且有几种常用的类型文本/plainimge/pngtext/pngtext/pngtext/ x-markdown 等等。

它还有两个子类别:

FormBody 这个请求体是最常用的一种。我们平时使用post请求时,参数都是键值对的形式。使用此请求正文是最简单的。

说得深一点,对应的请求消息是:

POST /测试 HTTP/1.1 请求行
Host: 32.106.24.148:8080 以下为请求头
Content-Type:application/x-www-form-urlencoded 用于指示请求体的类型。
用户代理:PostmanRuntime/7.15.0
接受:*/*
缓存控制:无缓存
邮递员令牌:954bda0d-dbc2-4193-addf-a7631cab2cfa,5ba2ebed-90b4-4f35-bcf5-80c4777de471
主机:39.106.24.148:8080
接受编码:gzip、deflate
内容长度:133
连接:保持活动
缓存控制:无缓存 key0=value0&key1=value1 请求体(也是我们的参数)

这是发送的原始消息格式。如果用代码实现的话就是

//创建客户端
OkHttpClient 客户端 = new OkHttpclient();
//创建请求体
FormBody formBody = new FormBody.Builder()
.add("key0", "value0")
.add("key1","value1")
.build();
//创建请求消息
请求请求=新的Request.Builder
.post(formBody)
.url("请求网址")
.addHeader("Content-Type", "application/x-www-form-urlencoded")
.addHeader("用户代理", "PostmanRuntime/7.15.0")
.addHeader("接受", "*/*")
.addHeader("缓存控制", "无缓存")
.addHeader("邮差令牌", "954bda0d-dbc2-4193-addf-a7631cab2cfa,af7c027c-a7ba-4560-98ae-3a2a473ab88a")
.addHeader("主机", "39.106.24.148:8080")
.addHeader("接受编码", "gzip, deflate")
.addHeader("内容长度", "133")
.addHeader("连接", "保持活动")
.addHeader("缓存控制", "无缓存")
.build();
// 发起请求
client.newCall(request).excute();

上面使用了FormBody的形式。如果使用RequestBody,那就比较麻烦了。

OkHttpClient 客户端 = new OkHttpClient(); MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
RequestBody body = RequestBody.create(mediaType, "key0=value0&key1=value1");
请求request = new Request.Builder()
.url("http://39.106.24.148:8080/test")
.post(正文)
.addHeader("Content-Type", "application/x-www-form-urlencoded")
.addHeader("用户代理", "PostmanRuntime/7.15.0")
.addHeader("接受", "*/*")
.addHeader("缓存控制", "无缓存")
.addHeader("邮差令牌", "954bda0d-dbc2-4193-addf-a7631cab2cfa,af7c027c-a7ba-4560-98ae-3a2a473ab88a")
.addHeader("主机", "39.106.24.148:8080")
.addHeader("接受编码", "gzip, deflate")
.addHeader("内容长度", "133")
.addHeader("连接", "保持活动")
.addHeader("缓存控制", "无缓存")
.build();
响应response = client.newCall(request).execute();

当然,我们平时使用的时候,不需要放那么多请求头。我写的目的是为了更方便的还原请求消息。

还有一个子类MultipartBody这可用于构建更复杂的请求主体。

1995 年,Content-Type 类型扩展为multipart/form-data,以支持向服务器发送二进制数据。如果一次提交多种类型的数据,比如图片、文字,此时就引入了boundaryboundary可以让POST满足提交多种不同数据的需求类型。 。通过boundary,一个Request中可以同时存在多种不同类型的数据。两个boundary之间是一种数据,可以重置Content-Type

兼容HTML文件上传表单。每个请求体都是一个请求体,可以定义自己的请求头。这些请求头可以用来描述这个请求。例如,他们的内容处置。如果可用,Content-Length 和 Content-Type 会自动添加到请求标头中。

我们来看看这种类型的请求消息是什么样的:

POST /web/UploadServlet HTTP/1.1
内容类型:多部分/表单数据;边界=e1b05ca4-fc4e-4944-837d-cc32c43c853a
内容长度:66089
主机:www.introzo.com:8080
连接:保持活动状态
接受编码:gzip
用户代理:okhttp/3.5.0 –e1b05ca4-fc4e-4944-837d-cc32c43c853a
内容处置:表单数据;名称=“文件”;文件名=“**.png”
内容类型:image/png
内容长度:65744 fdPNG
IHDR�0B7M�iM�M�CCPIM�CC ProfileH……………………IEND�B`�
–e1b05ca4-fc4e-4944-837d-cc32c43c853a
内容处置:表单数据;名称=“评论”
内容长度:30 上传图片
–e1b05ca4-fc4e-4944-837d-cc32c43c853a–

第一个数据是png图片,已经重置为Content-Type:image/png中间的乱码就是图片数据。这堆数据前面有一个空行,表示上下部分分别是请求头和请求体。

第二个数据是文本数据。

这样就共同组成了请求体。

说起来可能很复杂,但是请记住,当您需要上传参数和文件时,请使用此请求正文。

MediaType mediaType = MediaType.parse("image/png");
RequestBody requestBody = new MultipartBody.Builder()
// 需要设置为表单,否则键值对参数无法上传
.setType(MultipartBody.FORM)
.addPart(Headers.of("内容处置", "表单数据;name=\"标题\""),
RequestBody.create(null, "方形徽标"))
.addPart(
headers.of("Content-Disposition", "form-data;name=\"imge\""),
RequestBody.create(mediaType, new File("path/logo.png"))
).
构建();
请求 request = new Request.Builder()
.post(requestBody)
.url("https://www.introzo.com/3/image")
.build();
尝试一下{
mOkHttpClient.newCall(request).execute();
} catch (IOException e) {
e.printStackTrace();
}

简写:

MediaType mediaType = MediaType.parse("image/png");
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("标题","徽标")
.addFormDataPart("img","logo.png",RequestBody.create(mediaType,new File("path/logo.png")))
.build();

Content-Disposition 可用于消息正文的子部分,以提供有关其相应字段的信息。作为多部分正文中的消息头,第一个参数始终是固定的表单数据;附加参数不区分大小写并且具有参数值。参数名和参数值用等号连接,参数之间用分隔符分隔。用数字分隔。参数值用双引号括起来

//比如这是固定格式
"Content-Disposition","form-data;name=\"mFile\";filename=\"www.introzo.com4\""

对几个重要请求类别的讨论到此结束。

总结

只要了解了http请求的原理,使用okhttp就不成问题了。

首先,OkHttpClient用于设置请求工具的一些参数,比如超时、是否缓存等。

Call对象是发起Http请求的对象。请求是通过Call对象发起的。

发起请求时,需要请求消息。 Request对象就是对应的请求消息。您可以添加相应的请求行、请求头和请求体。

说到请求体,它对应于RequestBody。那么网络请求过程就完成了!

-->

关灯