从 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了。然后这个网络请求过程就完成了!
推荐阅读
-
淄川区基层畜牧兽医技术科技示范户喜领物化补贴
物化补助的发放,让科技示范户深切感受到了党和政府的温暖及基层农技推广体系改革与建设补助项目给他们带来的实惠,极大地提高了科技示范户参与项目建设的积极性,有效地提高了科技示范户的养殖水平,较好地辐射带动周边广大农户的畜牧兽医科技使用水平,充分发挥科技示范带动作用,促进全区畜牧兽医科技水平整体提高。为畜...
-
惠民兴县 | 中国康复医学会科技志愿服务望城行走进茶亭镇东城卫生院
志愿惠民兴县·康复惠民西部行为广泛组织动员广大科技志愿者和科技志愿服务组织深入学习贯彻习近平总书记关于弘扬雷锋精神的重要论述和重要指示精神,加强科技志愿服务组织建设,发挥标准化的基础性和引领性作用,进一步提升“惠民兴县”项目行动质效和社会影响力,中国科协宣传文化部和中国科协农村专业技术服务中心于3月...
-
大华黑体超详细拆解
为了给热成像做校正之前买的一个黑体不够用,这次想再买2个,遇到一个老板只打包不零卖,就用一个的价钱打包买了二十来个他们修过没修好的。收到货后东拼西凑弄好了5个,拆解的过程中发现大华的这玩意还挺有意思,比如型号同样是JQ-D70Z,从外表看却一个大一个小,温控表也一个大一个小。另外同样是大尺寸大表头的...
-
谁在粗制滥造?智能手环中的MEMS惯性传感器大有门道
如果说前几年消费电子市场的热点是是功能手机向智能手机的转换过渡,那么近几年则逐渐偏移到智能设备的便携化、智能化。近年来,国内外豪杰纷纷聚焦智能硬件,GoogleGlass问世,GalaxyGear接踵……今年9月份AppleWatch的亮相更是将这个热点推向极致。 这段...