294
技術社區[雲棲]
Java4Android之httpclient學習與應用
在Java開發中,不可避免的需要和http打交道。而無論我司的迅雷動漫還是我主導的“搜芽”android客戶端開發,都需要使用到http和服務器打交道。。雖然Java也提供了http的接口,但據我了解,更多的公司都是使用Apache的httpclient來進行開發,不僅因為它靈活強大,而且便捷。
今天,我們學習httpclient的基礎知識。
關於Http的基礎,在此就不再複習了。建議大家去看一本權威製作《HTTP權威指南》,加個略貴,109元人民幣,不過,我買了,因為經典的書,還是多備一下,我也沒怎麼看,但遇到問題就翻即可。
閑話少說,切入正題。
我們發出一個http的請求,在httpclient中一般如下流程模式:
1. 創建HttpClient對象。
2. 創建請求方法的實例,並指定請求URL。如果需要發送GET請求,創建HttpGet對象;如果需要發送POST請求,創建HttpPost對象。
3. 如果需要發送請求參數,可調用HttpGet、HttpPost共同的setParams(HetpParams params)方法來添加請求參數;對於HttpPost對象而言,也可調用setEntity(HttpEntity entity)方法來設置請求參數
4. 調用HttpClient對象的execute(HttpUriRequest request)發送請求,該方法返回一個HttpResponse。
5. 調用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可獲取服務器的響應頭;調用HttpResponse的getEntity()方法可獲取HttpEntity對象,該對象包裝了服務器的響應內容。程序可通過該對象獲取服務器的響應內容。
6. 釋放連接。無論執行方法是否成功,都必須釋放連接.
代碼示例如下:
CloseableHttpClient httpclient = HttpClients.createDefault(); HttpGet httpget = new HttpGet("https://localhost/"); CloseableHttpResponse response = httpclient.execute(httpget); try { <...> } finally { response.close(); }
請求:
HttpClient支持所有的HTTP/1.1的所有命令,包含:GET,HEAD,POST,PUT,DELETE,TRACE和OPTIONS。而且都有單獨對應的類:HttpGet,HttpHead,....等等。
請求URI是統一資源標識符,標識了你所要請求的資源。它一般包含protocol scheme , host name, optional port, resource path,optional query ,optional fragment這些信息。如:
HttpGet httpget = new HttpGet( "https://www.google.com/search?hl=en&q=httpclient&btnG=Google+Search&aq=f&oq=");構造上述URI一個更為好的方法是使用URIBuilder。具體如下:
URI uri = new URIBuilder() .setScheme("http") .setHost("www.google.com") .setPath("/search") .setParameter("q", "httpclient") .setParameter("btnG", "Google Search") .setParameter("aq", "f") .setParameter("oq", "") .build(); HttpGet httpget = new HttpGet(uri);
回複:
回複(response)是服務器對客戶端的回複。httpclient中的回複是HttpResponse.
HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); System.out.println(response.getProtocolVersion()); System.out.println(response.getStatusLine().getStatusCode()); System.out.println(response.getStatusLine().getReasonPhrase()); System.out.println(response.getStatusLine().toString());
輸出:
HTTP/1.1 200 OK HTTP/1.1 200 OK
消息頭部:
如果大家熟悉HTTP包的話,就知道一個HTTP報文是由三個部分組成的:對報文進行描述的起始行(start line),包含屬性的首部塊(header),以及可選的,包含數據的主體部分(body)。
httpclient的一個關於頭部的示例:
HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); response.addHeader("Set-Cookie", "c1=a; path=/; domain=localhost"); response.addHeader("Set-Cookie", "c2=b; path=\"/\", c3=c; domain=\"localhost\""); Header h1 = response.getFirstHeader("Set-Cookie"); System.out.println(h1); Header h2 = response.getLastHeader("Set-Cookie"); System.out.println(h2); Header[] hs = response.getHeaders("Set-Cookie"); System.out.println(hs.length);
其輸出結果:
Set-Cookie: c1=a; path=/; domain=localhost Set-Cookie: c2=b; path="/", c3=c; domain="localhost" 2
HTTP 實體(entity):
CloseableHttpClient httpclient = HttpClients.createDefault(); HttpGet httpget = new HttpGet("https://localhost/"); CloseableHttpResponse response = httpclient.execute(httpget); try { HttpEntity entity = response.getEntity(); if (entity != null) { InputStream instream = entity.getContent(); try { // do something useful } finally { instream.close(); } } } finally { response.close(); }
對實體的使用:
CloseableHttpClient httpclient = HttpClients.createDefault(); HttpGet httpget = new HttpGet("https://localhost/"); CloseableHttpResponse response = httpclient.execute(httpget); try { HttpEntity entity = response.getEntity(); if (entity != null) { long len = entity.getContentLength(); if (len != -1 && len < 2048) { System.out.println(EntityUtils.toString(entity)); } else { // Stream content out } } } finally { response.close(); }
那麼,如何產生一個實體呢:
File file = new File("somefile.txt"); FileEntity entity = new FileEntity(file, ContentType.create("text/plain", "UTF-8")); HttpPost httppost = new HttpP
List<NameValuePair> formparams = new ArrayList<NameValuePair>(); formparams.add(new BasicNameValuePair("param1", "value1")); formparams.add(new BasicNameValuePair("param2", "value2")); UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, Consts.UTF_8); HttpPost httppost = new HttpPost("https://localhost/handler.do"); httppost.setEntity(entity);會產生如下效果:
param1=value1&m2=value2
在最後,我給出我們對httpclient的GET和POST 的方法的一個封裝,這裏裏麵的代碼會將我們前麵講到的元素都涉及到。
private InputStream httpGet(String url, String cookie) { HttpGet httpGet = new HttpGet(url); httpGet.setHeader("Accept-Encoding", "gzip,deflate"); if (!(TextUtils.isEmpty(cookie))) { httpGet.setHeader("Cookie", cookie); } return httpDo(httpGet, url, null); } private InputStream httpPost(String url, Map<String, String> headers, Map<String, Object> params) { HttpPost post = new HttpPost(url); HttpEntity entity = null; Object value = params.get(POST_ENTITY); if (value instanceof HttpEntity) { entity = (HttpEntity) value; } else { List<NameValuePair> pairs = new ArrayList<NameValuePair>(); for (Map.Entry<String, Object> e : params.entrySet()) { value = e.getValue(); if (value != null) { LOG.debug("param=" + e.getKey() + ":" + value.toString()); pairs.add(new BasicNameValuePair(e.getKey(), value .toString())); } } try { entity = new UrlEncodedFormEntity(pairs, "UTF-8"); } catch (UnsupportedEncodingException e1) { LOG.warn("UnsupportedEncodingException err={}", e1.toString()); } } if (headers != null && !headers.containsKey("Content-Type")) { headers.put("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8"); } post.setEntity(entity); return httpDo(post, url, headers); } private InputStream httpDo(HttpUriRequest hr, String url, Map<String, String> headers) { InputStream in = null; if (headers != null) { for (String name : headers.keySet()) { hr.addHeader(name, headers.get(name)); } } DefaultHttpClient client = getClient(); HttpResponse response; try { response = client.execute(hr); int statusCode = response.getStatusLine().getStatusCode(); LOG.debug("this={}, response code={}", this, statusCode); if (statusCode == HttpStatus.SC_OK) { HttpEntity entity = response.getEntity(); if (null != entity) { Header header = entity.getContentEncoding(); if (header != null && header.getValue().equals("gzip")) { in = new GZIPInputStream(entity.getContent()); } else { in = entity.getContent(); } } } else { LOG.warn("Request HTTP resource failed. StatusCode={} Url={}", statusCode, url); } } catch (IOException e) { LOG.warn("Request HTTP resource failed. {}, err={}", this, e.toString()); } catch (IllegalStateException e) { LOG.warn("Request HTTP resource failed. url={} err={}", url, e.toString()); } return in; } private static DefaultHttpClient getClient() { HttpParams httpParams = new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(httpParams, CONNECTION_TIMEOUT); HttpConnectionParams.setSoTimeout(httpParams, SO_TIMEOUT); // https://stackoverflow.com/questions/5358014/android-httpclient-oom-on-4g-lte-htc-thunderbolt HttpConnectionParams.setSocketBufferSize(httpParams, 8192); return new DefaultHttpClient(httpParams); }
時間有限,今天到此為止。
最後更新:2017-04-03 05:40:19