从 http协议角度解析okhttp

Okhttp介绍

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

声明

支持spdy、、websocket等协议

支持同步、异步请求

封装了线程池,封装了数据转换,提高性能。

在中自带的网络请求API的底层就是使用了okhttp来进行的

使用okhttp比较接近真正的HTTP协议的框架

其他优点见:Android网络框架比较(后面更新)

说起okhttp的介绍,介绍完这几个关键类就可以了!

Okhttp中几个重要类的介绍

OkHttpClient

这个类主要是用来配置okhttp这个框架的,通俗一点讲就是这个类是管理这个框架的各种设置的。

Call类的工厂,通过OkHttpClient才能得到Call对象。

OkHttpClient使用注意

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

OkHttpClient的创建

OkHttpClient有三个创建方法

第一个方法:直接使用newOkHttpClient()来创建一个实例对象就可以了,这个实例对象有默认的配置。默认请求连接超时时间10s,读写超时时间10s,连接不成功会自动再次连接。

第二个方法:就是通过Builder的方式来自己定义一个OkHttpclient。当然如果你直接build没有自己配置参数的话,效果和第一个方法是一样的。

publicfinalOkHttpClient=().addInterceptor(newHttpLoggingInterceptor()).cache(newCache(cacheDir,cacheSize)).等等配置.build();

第三个方法:就是通过已有的OkHttpClient对象来复制一份共享线程池和其他资源的OkHttpClient对象。

OkHttpClientagerClient=().readTimeout(500,).build();

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

关于OkHttpClient的配置改变都在Builder中进行

不需要了可以关闭

其实持有的线程池和连接池将会被自定释放如果他们保持闲置的话。

你也可以自动释放,释放后将来再调用call的时候会被拒接。

().excurorService().shutdown()

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

().evictAll()

如果Client有缓存,可以关闭。注意:再次调用一个被关闭的cache会发生错误。也会造成crash。

().close();

OkHttp在HTTP/2连接的时候也会使用守护线程。他们闲置的时候将自动退出。

知道有这么一回事就行,一般不会主动调用。

Call类

Call这个类就是用来发送HTTP请求和读取HTTP响应的一个类

Call类方法.png

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

Request类

这个类就是相当于http请求中的请求报文,是用来表达请求报文的,所以这里可以设置请求的url、请求头、请求体等等和请求报文有关的内容。

主要方法罗列:

//获取请求urlpublicHttpUrlurl();//获取请求方法类型publicStringmethod();//获取请求头publicHeadersheaders();//获取请求体publicRequestBodybody();//获取tagpublicObjecttag();//返回缓存控制指令,永远不会是null,即使响应不包含Cache-Control响应头publicCacheControlcacheControl();//是否是https请求publicbooleanisHttps();//Resquest{method="",url="",tag=""}publicStringtoString();

request_

这是它的Builder中提供的方法,只设置.url()的时候默认是post请求。

RequestBody

介绍完请求报文就要介绍请求体了,这都是和http协议紧密联系的。

RequestBody就是用来设置请求体的,它的主要方法就是下面这个几个静态方法,用来生成对应的请求体:

request_

就是通过这几个方法来产生对应的不同的请求体。MediaType是用来描述请求体或者响应体类型的。比如请求体类型是json串格式的,那对应的MediaType就是("application/json;charset=utf-8");,如果上传的是文件那么对应的就是application/octet-stream,还有几个常用的类型text/plainimge/pngtext/x-markdown等等。

它还有两个子类:

request_

FormBody这个请求体是我们平时最常用的,就是我们平时使用post请求的时候,参数是键值对的形式。就是使用这个请求体最简单了。

说深一点,对应的请求报文是:

POST/testHTTP/1.1请求行Host:32.106.24.148:8080下面都是请求头Content-Type:application/x-www-form-urlencoded用于指明请求体的类型。User-Agent:PostmanRuntime/7.15.0Accept:*/*Cache-Control:no-cachePostman-Token:954bda0d-dbc2-4193-addf-a7631cab2cfa,5ba2ebed-90b4-4f35-bcf5-80c4777de471Host:39.106.24.148:8080accept-encoding:gzip,deflatecontent-length:133Connection:keep-alivecache-control:no-cachekey0=value0key1=value1请求体(也是我们的参数)

这是发送的原始的报文格式,用代码实现的话就是

//创建客户端OkHttpClientclient=newOkHttpclient();//建立请求体FormBodyformBody=().add("key0","value0").add("key1","value1").build();//建立请求报文Requestrequest=(formBody).url("请求url").addHeader("Content-Type","application/x-www-form-urlencoded").addHeader("User-Agent","PostmanRuntime/7.15.0").addHeader("Accept","*/*").addHeader("Cache-Control","no-cache").addHeader("Postman-Token","954bda0d-dbc2-4193-addf-a7631cab2cfa,af7c027c-a7ba-4560-98ae-3a2a473ab88a").addHeader("Host","39.106.24.148:8080").addHeader("accept-encoding","gzip,deflate").addHeader("content-length","133").addHeader("Connection","keep-alive").addHeader("cache-control","no-cache").build();//发起请求(request).excute();

上面是使用了FormBody的形式,如果使用RequestBody的话就要更麻烦一些。

OkHttpClientclient=newOkHttpClient();MediaTypemediaType=("application/x-www-form-urlencoded");RequestBodybody=(mediaType,"key0=value0key1=value1");Requestrequest=().url("").post(body).addHeader("Content-Type","application/x-www-form-urlencoded").addHeader("User-Agent","PostmanRuntime/7.15.0").addHeader("Accept","*/*").addHeader("Cache-Control","no-cache").addHeader("Postman-Token","954bda0d-dbc2-4193-addf-a7631cab2cfa,af7c027c-a7ba-4560-98ae-3a2a473ab88a").addHeader("Host","39.106.24.148:8080").addHeader("accept-encoding","gzip,deflate").addHeader("content-length","133").addHeader("Connection","keep-alive").addHeader("cache-control","no-cache").build();Responseresponse=(request).execute();

当然平时我们使用的时候,不用拼上这么多的请求头,我这样写的目的就是为了更加还原请求报文。

还有一个子类MultipartBody这个可以用来构建比较复杂的请求体。

1995年Content-Type的类型扩充了multipart/form-data用来支持向服务器发送二进制数据。如果一次提交多种类型的数据,比如:一张图片和一个文字,这个时候引入了boundary,boundary使得POST可以满足这种提交多种不同的数据类型。通过boundary可以实现多个不同类型的数据同时存在在一个Request中。两个boundary之间就是一个类型的数据,并且可以重新设置Content-Type

与HTML文件上传形式兼容。每块请求体都是一个请求体,可以定义自己的请求头。这些请求头可以用来描述这块请求。例如,他们的Content-Disposition。如果Content-Length和Content-Type可用的话,他们会被自动添加到请求头中。

来看一下这种类型的请求报文是什么样的:

POST/web/UploadServletHTTP/1.1Content-Type:multipart/form-data;boundary=e1b05ca4-fc4e-4944-837d-cc32c43c853aContent-Length:66089Host::8080Connection:Keep-AliveAccept-Encoding:gzipUser-Agent:okhttp/3.5.0–e1b05ca4-fc4e-4944-837d-cc32c43c853aContent-Disposition:form-data;name=”file”;filename=”**.png”Content-Type:image/pngContent-Length:65744fdPNGIHDR�0B7M�iM�M�CCPIM�CCProfileH��……………………IEND�B`�–e1b05ca4-fc4e-4944-837d-cc32c43c853aContent-Disposition:form-data;name=”comment”Content-Length:30上传一个图–e1b05ca4-fc4e-4944-837d-cc32c43c853a–

第一个数据是一张png的图,重新设置了Content-Type:image/png中间的乱码就是图片的数据。这一堆数据前有一个空行,表示上下分别是请求头、请求体。

第二个数据,就是一个文本数据。

这样它们一起构成了请求体。

讲起来可能比较复杂,就记住,当既需要上传参数,又需要上传文件的时候用这种请求体。

MediaTypemediaType=("image/png");RequestBodyrequestBody=()//需要设置成表单形式否则无法上传键值对参数.setType().addPart(("Content-Disposition","form-data;name=\"title\""),(null,"SquareLogo")).addPart(("Content-Disposition","form-data;name=\"imge\""),(mediaType,newFile("路径/"))).build();Requestrequest=().post(requestBody).url("").build();try{(request).execute();}catch(IOExceptione){();}

简化写法:

MediaTypemediaType=("image/png");RequestBodyrequestBody=().setType().addFormDataPart("title","logo").addFormDataPart("img","",(mediaType,newFile("路径/"))).build();

Content-Disposition可以用在消息体的子部分中,用来给出其对应字段的相关信息。作为multipartbody中的消息头,第一个参数总是固定不变的form-data;附加的参数不区分大小写,并且拥有参数值,参数名与参数值用等号连接,参数之间用分号分隔。参数值用双引号括起来

//比如这样,就是这种固定的格式"Content-Disposition","form-data;name=\"mFile\";filename=\"\""

到这里关于请求的几个重要的类就讲完了。

总结一下

只要掌握http请求的原理,使用起okhttp来也就不是什么问题了。

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

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

发起请求的时候,需要有请求报文,Request对象就是对应的请求报文,可以添加对应的请求行、请求头、请求体。

说起请求体就是对应了RequestBody了。然后这个网络请求过程就完成了!

发布于 2025-05-04
160
目录

    推荐阅读