1)使用HttpEntity#getContent()方法来检索输入流,返回java.io.InputStream。
2)为HttpEntity#writeTo(OutputStream)方法提供一个输出流,当所有内容被写入指定的流后才会返回一次。
当实体随着一个输入消息被接收到,HttpEntity#getContentType()和HttpEntity#getContentLength()这两个方法可以用来读公共的元数据如:Content-Type头和Content-Length头(如何它们可获得)。Content-Type头可以为文本类型(如text/plain或text/html)包含字符编码,而HttpEntity#getContentEncoding()方法是用来读取这个信息的。如果头不能使用,将返回-1长度,同时内容类型为NULL。如果Content-Type头可获得,将返回头对象。
当创建一个输出消息时,元数据必需由实体创造者提供。
StringEntitymyEntity = new StringEntity(“important message”,
ContentType.create(“text/plain”,“UTF-8”));
System.out.println(myEntity.getContentType());
System.out.println(myEntity.getContentLength());
System.out.println(EntityUtils.toString(myEntity));
System.out.println(EntityUtils.toByteArray(myEntity).length);
输出>
Content-Type:text/plain; charset=utf-8
17
importantmessage
17
1.1.5保证释放底层资源
为了确保系统资源的正确释放,必需要至少关闭以下两种情况的其中之一:
1)与实体关联的内容流;
2)自身的应答。
CloseableHttpClienthttpclient = HttpClients.createDefault();
HttpGethttpget = new HttpGet(“http://localhost/”);
CloseableHttpResponseresponse = httpclient.execute(httpget);
try{
HttpEntityentity = response.getEntity();
if(entity != null) {
InputStreaminstream = entity.getContent();
try{
//do something useful
}finally {
instream.close();
}
}
}finally {
response.close();
}
关闭stream和关闭response的区别是,是否框架会由于消耗实体内容尝试保持底层连接,而后者会立即关闭和丢弃连接。
请注意HttpEntity#writeTo(OutputStream)方法也需要保证实体被完全写出后能正确地释放系统资源。如果这个方法是通过调用HttpEntity#getContent()来获得java.io.InputStream实例的,最后也是要关闭的。
使用流实体时,可以使用EntityUtils#consume(HttpEntity)方法来保证实体内容已被完全消耗和底层流被关闭。
这是一种情况,不管怎么样,只有实体应答的一小部分连接需要恢复和损失性能为了消耗剩下连接和制造连接可复用性的代价是非常高的,在这种情况下可以通过关闭应答来结束内容流。
CloseableHttpClienthttpclient = HttpClients.createDefault();
HttpGethttpget = new HttpGet(“http://localhost/”);
CloseableHttpResponseresponse = httpclient.execute(httpget);
try{
HttpEntityentity = response.getEntity();
if(entity != null) {
InputStreaminstream = entity.getContent();
intbyteOne = instream.read();
intbyteTwo = instream.read();
//Do not need the rest不需要等待
}
}finally {
response.close();
}
这个连接不能被重新使用,不过所有等级的资源会因为被正确分配而被保持。
1.1.6消费的实体连接
推荐使用HttpEntity#getContent()或HttpEntity#writeTo(OutputStream)方法来消费实体的内容。HttpClient也会伴随EntityUtils类发生,EntityUtils提供了几个能轻松在实体里读内容或信息的静态方法。你可以通过使用这个类的方法来将整个内容体恢复为string/ byte array(字符串或字节阵列)。然而,强烈反对使用EntityUtils,除非应答是来自可信的HTTP服务器和在限制长度内的答应。
CloseableHttpClienthttpclient = HttpClients.createDefault();
HttpGethttpget = new HttpGet(“http://localhost/”);
CloseableHttpResponseresponse = httpclient.execute(httpget);
try{
HttpEntityentity = response.getEntity();
if(entity != null) {
longlen = entity.getContentLength();
if(len != -1 && len < 2048) {
System.out.println(EntityUtils.toString(entity));
}else {
//Stream content out
}
}
}finally {
response.close();
}
在某些情况,可能会需要多次读实体。既然这样,实体内容必需要保存在内存或磁盘里。最简单的方法是使用BufferedHttpEntity类将源实体包装起来。这样就能从内存缓冲区里读源实体的内容了。在所有其它的实体包装方式里都会有一个源。
CloseableHttpResponseresponse = <…>
HttpEntityentity = response.getEntity();
if(entity != null) {
entity= new BufferedHttpEntity(entity);
}
1.1.7产生实体连接
HttpClient提供了几个类通过HTTP连接来高效输出’流’的内容。这些类的实例可以被实体联系上,并附上请求(如POST和PUT),从而为了在将实体内容附进输出的HTTP申请。HttpClient为大多数的普通数据容器如string,byte array, input stream, 和file:StringEntity,ByteArrayEntity,InputStreamEntity,和FileEntity提供了几个类。
Filefile = new File(“somefile.txt”);
FileEntityentity = new FileEntity(file,
ContentType.create(“text/plain”,“UTF-8”));
HttpPosthttppost = new HttpPost(“http://localhost/action.do”);
httppost.setEntity(entity);
请注意InputStreamEntity是不可反复使用的,它只能从底层数据流读取一次。通常推荐实例一个定制的HttpEntity类,使用self-contained来代替使用普通的InputStreamEntity.FileEntity。
1.1.7.1HTML框架
许多应用需要模拟HTML框架的过程,比如,为了从应用里登陆或提交输入数据。为帮助这个工作过程,HttpClient提供了实体类UrlEncodedFormEntity。
Listformparams = new ArrayList();
formparams.add(newBasicNameValuePair(“param1”, “value1”));
formparams.add(newBasicNameValuePair(“param2”, “value2”));
UrlEncodedFormEntityentity = new UrlEncodedFormEntity(formparams, Consts.UTF_8);
HttpPosthttppost = new HttpPost(“http://localhost/handler.do”);
httppost.setEntity(entity);
UrlEncodedFormEntity实例使用这样调用URL编码方式来编码参数,并产生如下内容:
param1=value1¶m2=value2
1.1.7.2内容分块
通常推荐让HttpClient选择最合适的基于HTTP消息特性的传输编码。这是可能的,然而,可以通过设置HttpEntity#setChunked()为true来通知HttpClient首先使用分块编码。请注意,HttpClient用这个flag仅作为一个暗示。当使用HTTP协议版本不支持块编码(如HTTP/1.0)时,这个值会被忽略。
StringEntityentity = new StringEntity(“important message”,
ContentType.create(“plain/text”,Consts.UTF_8));
entity.setChunked(true);
HttpPosthttppost = new HttpPost(“http://localhost/acrtion.do”);
httppost.setEntity(entity);
1.1.8应答操纵器
最简单和最方便的操纵应答方法是使用ResponseHandler接口,它包括handleResponse(HttpResponseresponse)方法。这个方法完全地解除了用户对连接管理的麻烦。当使用ResponseHandler时,HttpClient会自动地去关心保证资源释放(连接管理者不顾后果地连接返回不管是否请求成功或引起异常)。
CloseableHttpClienthttpclient = HttpClients.createDefault();
HttpGethttpget = new HttpGet(“http://localhost/json”);
ResponseHandlerrh = new ResponseHandler() {
@Override
publicJsonObject handleResponse(
finalHttpResponse response) throws IOException {
StatusLinestatusLine = response.getStatusLine();
HttpEntityentity = response.getEntity();
if(statusLine.getStatusCode() >= 300) {
thrownew HttpResponseException(
statusLine.getStatusCode(),
statusLine.getReasonPhrase());
}
if(entity == null) {
thrownew ClientProtocolException(“Response contains no content”);
}
Gsongson = new GsonBuilder().create();
ContentTypecontentType = ContentType.getOrDefault(entity);
Charsetcharset = contentType.getCharset();
Readerreader = new InputStreamReader(entity.getContent(), charset);
returngson.fromJson(reader, MyJsonObject.class);
}
};
MyJsonObjectmyjson = client.execute(httpget, rh);
HttpClient接口为HTTP请求实行描绘了最基本了协议。它推行:
1)不受限制;
2)详细的请求执行过程;
3)指定连接管理员、正确的管理、鉴定和举起独特的执行情况。
这会将会更容易用额外的功能(如应答内容缓存)来装饰接口。
通常HttpClient执行情况担当许多特别意图处理器的外表或策略接口执行情况答应,为了对HTTP协议(如更改、鉴定处理、做关于连接持续连接决定和保持连接)的特别方面的进行处理。这能让使用者有选择性地替代那些定制的、应用程序明确的默认执行情况。
ConnectionKeepAliveStrategykeepAliveStrat = new DefaultConnectionKeepAliveStrategy() {
@Override
publiclong getKeepAliveDuration(
HttpResponseresponse,
HttpContextcontext) {
longkeepAlive = super.getKeepAliveDuration(response, context);
if(keepAlive == -1) {
//Keep connections alive 5 seconds if a keep-alive value
//has not be explicitly set by the server
keepAlive= 5000;
}
returnkeepAlive;
}
};
CloseableHttpClienthttpclient = HttpClients.custom()
.setKeepAliveStrategy(keepAliveStrat)
.build();
1.2.1HttpClient线程安全
推荐在多请求执行的情况下重复使用同一个实例。
1.2.2HttpClient应答存储单元分配
当一个CloseableHttpClient实例不再需要和即将离开管理员关联文件范围就必须使用CloseableHttpClient#close()方法来完全关闭。
CloseableHttpClienthttpclient = HttpClients.createDefault();
try{
<…>
}finally {
httpclient.close();
}
最初HTTP是被设计成无状态的,以应答-请求为导向的协议。然而,真实情况是应用往往需要通过几个逻辑上相关的请求-应答交换来确认状态信息。为了让应用保持一个处理中的状态,HttpClient允许HTTP请求被执行在一个特别的上下文里,被称为HTTP上下文。如果同样的上下文在连续的请求之间被复用,多个逻辑关系请求会加入逻辑session。HTTP上下文功能类似于java.util.Map<String,Object>.。它简单地收集了任意指定的名字和对应的值。应用可以填入上下文的属性去优先请求执行或在执行完后检查上下文。
HttpContext可以包含随意的对象。因此,在多线程间分享是不安全的。推荐每个线程维护自己的上下文。
在HTTP请求执行期间,HttpClient添加了以下特性去执行上下文:
HttpConnection``实例代表真实的目标服务器连接。
HttpHost``实例代表连接目标。
HttpRoute``实例代表完整的连接通路。
HttpRequest``实例代表现在的``HTTP``请求。在执行上下里的最终``HttpRequest``对象代表消息的状态总和发送给目标服务器的一样。依照默认的``HTTP/1.0``和``HTTP/1.1``使用相关的``URI``。然而如果这个请求是通过在``non-tunneling``模式下代理发送的,那么``URI``将会是绝对的。
HttpResponse``实例代表现在的``HTTP``应答。
java.lang.Boolean``对象代表指示标记是否现在的请求是否已经完全传给了连接目标。
RequestConfig``对象代表现在的请求的配置。
java.util.List<URI>``对象代表一个在请求执行过程中被收到的所有的
重定向位置集合。
可以使用HttpClientContext``适配器类简化与上下文状态的互动。
HttpContext context = <…> //
译者提醒:可这样创建
HttpContext context = new BasicHttpContext();
HttpClientContext clientContext = HttpClientContext.adapt(context);
HttpHost target = clientContext.getTargetHost();
HttpRequest request = clientContext.getRequest();
HttpResponse response = clientContext.getResponse();
RequestConfig config = clientContext.getRequestConfig();
多个请求序列代表一个
logicallyrelated session 与``HttpContext``实例的对话上下文和状态信息间的请求确保自动传播是一样的。
在以下例子里请求配置被初始化并被保存在执行上下文里,在连续执行请求时使用同一个上下文,配置就会相同。
CloseableHttpClient httpclient = HttpClients.createDefault();
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(1000)
.setConnectTimeout(1000)
.build();
HttpGet httpget1 = new HttpGet(“http://localhost/1”);
httpget1.setConfig(requestConfig);
CloseableHttpResponse response1 = httpclient.execute(httpget1, context);
try {
HttpEntity entity1 = response1.getEntity();
} finally {
response1.close();
}
HttpGet httpget2 = new HttpGet(“http://localhost/2”);
CloseableHttpResponse response2 = httpclient.execute(httpget2, context);
try {
HttpEntity entity2 = response2.getEntity();
} finally {
response2.close();
}
HttpClient可以抛出两种类型异常。
1)java.io.IOException:I/O失败时抛出,如:socket超时或重置socket;
2)HttpException:向HTTP发信号失败,如:违反HTTP协议。
I/O错误通常都要注重非致命性和可恢复性,然而HTTP协议错误要注意它是致命性并不能从错误里恢复。
1.4.1HTTP传输安全性
要重点知道的是,HTTP协议并不是对于所有类型应用都适用的。HTTP是一个以请求/应答为导向的简单的协议。它起初被设计成支持静态或动态生成内容检索。它从没被刻意去支持事务操作。例如,HTTP服务器会注重它的协议处理部分,如成功接收并处理请求、生成应答和给客户端回送状态码。服务器不会回滚事务,即使客户端由于读超时、请求取消或系统崩溃而接受应答失败。如果客户端决定再尝试同样的请求,服务器必然会结束执行同样的事务。有时候,这可能会导致应用数据腐化或应用状态不一致。
既然HTTP没有被设计成支持事务处理,但它仍可以作为传输协议,为应用的关键任务提供确定的对话状态。为了保证HTTP传输层安全,系统必需保证HTTP方法在应用层上的幂等性。
1.4.2幂等方法
HTTP/1.1这样定义幂等方法:方法可以也可以有幂等性,在N>0次请求和一次请求的副作用(除非错误或过期)一样。
就是说,应用应当保证愿意去处理多次执行同样方法的所引发的后果。这是可以实现的,例如,通过提供唯一的传输ID和使用其它手段避免执行同样的逻辑操作。
请注意这个问题不是HttpClient独有的。基于应用的浏览器为了与非幂等的HTTP方法相协调,限制了完全一样的问题。
HttpCient假如非实体装入方法如GET和HEAD将是幂等的,实体装入方法如POST和PUT就不是。
1.4.3自动异常恢复
通过默认的HttpClient尝试从I/O异常里自动恢复。默认的自动恢复机制会被即将知道的少许异常所限制:
*HttpClient不会尝试从逻辑或HTTP协议错误(那些采自HttpException类)里恢复。
*HttpClient会自动重新尝试这些将成为幂等的假定的方法。
*HttpClient会自动重新尝试,当HTTP请求还在传输给服务器(请求没有完全传输给服务器)时,并随着传输异常而失败的方法。
1.4.4请求的重新尝试处理器
为了使能自定义的异常恢复机构,你应该提供一个HttpRequestRetryHandler接口的实现。
HttpRequestRetryHandlermyRetryHandler = new HttpRequestRetryHandler() {
publicboolean retryRequest(
IOExceptionexception,
intexecutionCount,
HttpContextcontext) {
if(executionCount >= 5) {
//Do not retry if over max retry count
returnfalse;
}
if(exception instanceof InterruptedIOException) {
//Timeout
returnfalse;
}
if(exception instanceof UnknownHostException) {
//Unknown host
returnfalse;
}
if(exception instanceof ConnectTimeoutException) {
//Connection refused
returnfalse;
}
if(exception instanceof SSLException) {
//SSL handshake exception
returnfalse;
}
HttpClientContextclientContext = HttpClientContext.adapt(context);
HttpRequestrequest = clientContext.getRequest();
booleanidempotent = !(request instanceof HttpEntityEnclosingRequest);
if(idempotent) {
//Retry if the request is considered idempotent
returntrue;
}
returnfalse;
}
};
CloseableHttpClienthttpclient = HttpClients.custom()
.setRetryHandler(myRetryHandler)
.build();
在某些情况HTTP会请求执行失败:服务器端在预期的时间内超负载或当前太多客户端请求。在这样的情况下,可能需要停止请求,在一个I/O操作下贸然地解除执行的线程阻塞。HttpClient可以调用HttpUriRequest#abort()方法中止在何意的执行状态,以保证HTTP请求继续执行。这个方法是线程安全的并可以被任意的线程调用。以下情况里执行线程可中断一个HTTP请求:1)当前阻塞在I/O操作内;2)抛出InterruptedIOException异常而被担保去除阻塞。
HTTP协议拦截机是HTTP协议的一套具体的实现方法。通常协议拦截机被希望去对一个(或一群)明确的输入消息标头(或标头关系群)或用一个(或一群)明确的输出消息标头(或标头关系群)起作用。协议拦截机也可以操作被附上内容实体的消息——传输内容的压缩/解压缩就是一个好例子。通常,这要像“油漆工”的一样,通过封装实体类来装饰源实体。几个协议拦截机可以被结合形成一个逻辑单元。
协议拦截机可以通过分享信息来协作——如过程状态——通过HTTP执行上下文。协议拦截机可以使用HTTP上下文来为一个请求或几个连续的请求存储一个过程状态
HttpClient会自动地操作所有重定向,除了那些被HTTP规范明确禁止用户干预的请求。在HTTP规范里必需将状态码为303的POST和PUT请求重定向为GET请求。你可以使用一个自定义的重定向策略来放宽HTTP规范对POST方法的自动重定向限制。
LaxRedirectStrategyredirectStrategy = new LaxRedirectStrategy();
CloseableHttpClienthttpclient = HttpClients.custom()
.setRedirectStrategy(redirectStrategy)
.build();
HttpClient常常在执行过程中必须去重写请求消息。默认的HTTP/1.0和HTTP/1.1普遍都会使用关系请求URI。使用源请求和上下文可以建立最终被解释的绝对HTTP位置。URIUtils#resolve``可以被用来建立解释绝对``URI``并形成最终的请求。
这个方法包含来自重定向请求或源请求的最后片段标识。
CloseableHttpClient httpclient = HttpClients.createDefault();
面试复习笔记:
这份资料我从春招开始,就会将各博客、论坛。网站上等优质的Android开发中高级面试题收集起来,然后全网寻找最优的解答方案。每一道面试题都是百分百的大厂面经真题+最优解答。包知识脉络 + 诸多细节。
节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
《960页Android开发笔记》
《1307页Android开发面试宝典》
包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。
《507页Android开发相关源码解析》
只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。
真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
UT请求重定向为GET请求。你可以使用一个自定义的重定向策略来放宽HTTP规范对POST方法的自动重定向限制。
LaxRedirectStrategyredirectStrategy = new LaxRedirectStrategy();
CloseableHttpClienthttpclient = HttpClients.custom()
.setRedirectStrategy(redirectStrategy)
.build();
HttpClient常常在执行过程中必须去重写请求消息。默认的HTTP/1.0和HTTP/1.1普遍都会使用关系请求URI。使用源请求和上下文可以建立最终被解释的绝对HTTP位置。URIUtils#resolve``可以被用来建立解释绝对``URI``并形成最终的请求。
这个方法包含来自重定向请求或源请求的最后片段标识。
CloseableHttpClient httpclient = HttpClients.createDefault();
面试复习笔记:
这份资料我从春招开始,就会将各博客、论坛。网站上等优质的Android开发中高级面试题收集起来,然后全网寻找最优的解答方案。每一道面试题都是百分百的大厂面经真题+最优解答。包知识脉络 + 诸多细节。
节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
《960页Android开发笔记》
[外链图片转存中…(img-LadwC7aJ-1714975193218)]
《1307页Android开发面试宝典》
包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。
[外链图片转存中…(img-9ZjnFaQN-1714975193219)]
《507页Android开发相关源码解析》
只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。
真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!