閱讀294 返回首頁    go 技術社區[雲棲]


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):

在httpclient中存在三種實體,streamed,self-contained和wrapping。它們的區別在我們用到的時候再區分,一般而言,流對象適合接收流數據,self-contained自包含適合那些可重複讀取的場景。wrapping是對已有實體的一個包裝。
下麵是一個使用實體的例子:
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();
}

那麼,如何產生一個實體呢:
實體有分很多種類,所以httpclient也提供了幾個產生實體的類,分別產生對應的實體。StringEntity,ByteArrayEntity,InputStreamEntity和FileEntity,它們分別產生string, byte array, input stream ,file。一個fileEntity的示例如下:
File file = new File("somefile.txt");
FileEntity entity = new FileEntity(file,
ContentType.create("text/plain", "UTF-8"));
HttpPost httppost = new HttpP

表單:
httpclient也提供了類似與http表單的功能,比如用戶登入頁麵,需要用戶名和密碼。
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

  上一篇:go Java4Android之HttpClient入門使用代碼集
  下一篇:go C/C++下void*類型指針介紹