Java Android HTTP实现总结
Http(Hypertext Transfer Protocol)超文本传输协议,是一个基于请求/响应模式的无状态的协议,Http1.1版给出了持续连接的机制,客户端建立连接之后,可以发送多次请求,当不会再发送时再关闭连接。
Android使用Java,对于Http协议的基本功能有两种实现方案:
1.使用JDK的java.net包下的HttpURLConnection.
2.使用Apache的HttpClient。
关于二者的比较可以看一下:
Android SDK中集成了Apache的HttpClient模块,也即说Android上两种方法都能用。
之前看一个Android开发者博客(原文链接先空缺,需要FQ)对此的讨论,大意总结如下:
1.HttpClient的功能比较全,更加强大;而HttpURLConnection的功能较简单和原始,但是性能更好。
2.在Android 2.x的版本中使用HttpURLConnection有bug,但是后来高级版本的Android已经将带来的bug修复,并且做了一些进一步优化的工作,所以建议在高级版本的Android系统(Android 2.3之后)使用HttpURLConnection,低版本的系统仍使用HttpClient。
程序实现
下面来讨论一下实现,首先,需要确认Manifest中有权限:
使用JDK的HttpURLConnection类
HttpURLConnection参考:
使用这个类的一般步骤:
1.通过 方法获取一个HttpURLConnection对象,并且将结果强制转化为HttpURLConnection类型。
2.准备请求(prepare the request),包括URI,headers中的各种属性等
(Request headers may also include metadata such as credentials, preferred content types, and session cookies.)
3.请求体(optionally)。如果有请求体那么必须为true,然后把输入放在流中。
4.读取响应。响应的headers一般包括了一些metadata比如响应体的内容类型和长度,修改日期以及session cookies。响应体可以从 流中读出。
5.断开连接。响应体被读出之后,应该调用 方法来断开连接。
例子代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
package com.example.helloandroidhttp;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.io.UnsupportedEncodingException;import java.net.HttpURLConnection;import java.net.MalformedURLException;import java.net.URL;import java.net.URLEncoder;import java.util.Map;import android.util.Log;public class HttpUtilsJDK { private static final String LOG_TAG = "Http->JDK"; private static final int CONNECT_TIME_OUT = 3000; private static final String HEADER_CONTENT_TYPE = "Content-Type"; private static final String HEADER_CONTENT_LENGTH = "Content-Length"; /** * Default encoding for POST or PUT parameters. See * { @link #getParamsEncoding()}. */ private static final String DEFAULT_PARAMS_ENCODING = "UTF-8"; public static String getParamsEncoding() { return DEFAULT_PARAMS_ENCODING; } public static String getBodyContentType() { return "application/x-www-form-urlencoded; charset=" + getParamsEncoding(); } public static String performGetRequest(String baseUrl) { String result = null; HttpURLConnection connection = null; try { URL url = new URL(baseUrl); if (null != url) { // 获取HttpURLConnection类型的对象 connection = (HttpURLConnection) url.openConnection(); // 设置连接的最大等待时间 connection.setConnectTimeout(CONNECT_TIME_OUT); // Sets the maximum time to wait for an input stream read to // complete before giving up. connection.setReadTimeout(3000); // 设置为GET方法 connection.setRequestMethod("GET"); connection.setDoInput(true); if (200 == connection.getResponseCode()) { InputStream inputStream = connection.getInputStream(); result = getResultString(inputStream, getParamsEncoding()); } else { Log.e(LOG_TAG, "Connection failed: " + connection.getResponseCode()); } } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { connection.disconnect(); } return result; } public static String performPostRequest(String baseUrl, Mapparams) { String result = null; HttpURLConnection connection = null; try { URL url = new URL(baseUrl); if (null != url) { // 获取HttpURLConnection类型的对象 connection = (HttpURLConnection) url.openConnection(); // 设置响应超时限制 connection.setConnectTimeout(CONNECT_TIME_OUT); // 设置为POST方法 connection.setRequestMethod("POST"); connection.setDoInput(true); // 有请求体则setDoOutput(true)必须设定 connection.setDoOutput(true); // 为了性能考虑,如果包含请求体,那么最好调用 setFixedLengthStreamingMode(int)或者 // setChunkedStreamingMode(int) // connection.setChunkedStreamingMode(0);// 参数为0时使用默认值 byte[] data = getParamsData(params); connection.setRequestProperty(HEADER_CONTENT_TYPE, getBodyContentType()); if (null != data) { connection.setFixedLengthStreamingMode(data.length); connection.setRequestProperty(HEADER_CONTENT_LENGTH, String.valueOf(data.length)); OutputStream outputStream = connection.getOutputStream(); outputStream.write(data); } // 得到返回值 int responseCode = connection.getResponseCode(); if (200 == responseCode) { result = getResultString(connection.getInputStream(), getParamsEncoding()); } else { Log.e(LOG_TAG, "Connection failed: " + connection.getResponseCode()); } } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { connection.disconnect(); } return result; } private static byte[] getParamsData(Map params) { byte[] data = null; try { if (null != params && !params.isEmpty()) { StringBuffer buffer = new StringBuffer(); for (Map.Entry entry : params.entrySet()) { buffer.append(entry.getKey()) .append("=") .append(URLEncoder.encode(entry.getValue(), getParamsEncoding())).append("&");// 请求的参数之间使用&分割。 } // 最后一个&要去掉 buffer.deleteCharAt(buffer.length() - 1); data = buffer.toString().getBytes(getParamsEncoding()); } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return data; } private static String getResultString(InputStream inputStream, String encode) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); byte[] data = new byte[1024]; int len = 0; String result = ""; if (inputStream != null) { try { while ((len = inputStream.read(data)) != -1) { outputStream.write(data, 0, len); } result = new String(outputStream.toByteArray(), encode); } catch (IOException e) { e.printStackTrace(); } } return result; }}
使用Apache的HttpClient
可以查看官方的Tutorial:
Android有一个实现类AndroidHttpClient,实现了HttpClient:
包装了一些默认的设置。
关于HTTP entity
HTTP消息中可以包含内容实体(content entity),可以看做消息的报文,包含在请求或者响应中。
HTTP规范规定两种请求方法可以包含内容实体:POST和PUT。
响应则通常是包含内容实体的。
HttpClient会根据内容来源区分三种实体:
1.streamed:内容来源是流,这类里包含了从HTTP响应中获得的实体,流式实体不可重复。
2.self-contained:内容是从内存或者其他方式获得的,即和连接无关,这类实体是可以重复的,多数是用来放在HTTP请求中的实体。
3.wrapping:这类实体是从其他实体获得的。
对于用HttpClient创建的请求实体来说,streamed和self-contained类型的区别其实不太重要,建议把不可重复的实体看作是streamed的,可重复的看作是self-contained的。
创造实体内容
为了发送HTTP的POST请求(当然还有PUT请求也有实体),需要把一些参数放在实体中,创造实体内容,有四个类型的类可选用:
StringEntity, ByteArrayEntity, InputStreamEntity, FileEntity
注意其中的InputStreamEntity是不可重复的。
UrlEncodedFormEntity这个类是用来把输入数据编码成合适的内容,比如下面这段:
Listformparams = new ArrayList ();formparams.add(new BasicNameValuePair("param1", "value1"));formparams.add(new BasicNameValuePair("param2", "value2"));UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, Consts.UTF_8);HttpPost httppost = new HttpPost("http://localhost/handler.do");httppost.setEntity(entity);
两个键值对,被UrlEncodedFormEntity实例编码后变为如下内容:
param1=value1¶m2=value2
使用Apache的HttpClient发送HTTP请求的辅助类,例子代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
package com.example.helloandroidhttp;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.UnsupportedEncodingException;import java.net.URLEncoder;import java.util.ArrayList;import java.util.List;import java.util.Map;import org.apache.http.HttpEntity;import org.apache.http.HttpResponse;import org.apache.http.NameValuePair;import org.apache.http.client.HttpClient;import org.apache.http.client.entity.UrlEncodedFormEntity;import org.apache.http.client.methods.HttpGet;import org.apache.http.client.methods.HttpPost;import org.apache.http.entity.ByteArrayEntity;import org.apache.http.entity.StringEntity;import org.apache.http.impl.client.DefaultHttpClient;import org.apache.http.message.BasicNameValuePair;import org.apache.http.params.BasicHttpParams;import org.apache.http.params.HttpConnectionParams;import org.apache.http.params.HttpParams;import android.util.Log;public class HttpUtilsApache { private static final String LOG_TAG = "Http->Apache"; private static final String HEADER_CONTENT_TYPE = "Content-Type"; /** * Default encoding for POST or PUT parameters. See * { @link #getParamsEncoding()}. */ private static final String DEFAULT_PARAMS_ENCODING = "UTF-8"; /** * Returns which encoding should be used when converting POST or PUT * parameters returned by { @link #getParams()} into a raw POST or PUT body. * ** This controls both encodings: *
*
*/ public static String getParamsEncoding() { return DEFAULT_PARAMS_ENCODING; } public static String getBodyContentType() { return "application/x-www-form-urlencoded; charset=" + getParamsEncoding(); } public static String performGetRequest(String url) { String result = null; // 生成一个请求对象 HttpGet httpGet = new HttpGet(url); // 1.生成一个Http客户端对象(带参数的) HttpParams httpParameters = new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(httpParameters, 10 * 1000);// 设置请求超时10秒 HttpConnectionParams.setSoTimeout(httpParameters, 10 * 1000); // 设置等待数据超时10秒 HttpConnectionParams.setSocketBufferSize(httpParameters, 8192); HttpClient httpClient = new DefaultHttpClient(httpParameters); // 此时构造DefaultHttpClient时将参数传入 // 2.默认实现: // HttpClient httpClient = new DefaultHttpClient(); httpGet.addHeader(HEADER_CONTENT_TYPE, getBodyContentType()); // 下面使用Http客户端发送请求,并获取响应内容 HttpResponse httpResponse = null; try { // 发送请求并获得响应对象 httpResponse = httpClient.execute(httpGet); final int statusCode = httpResponse.getStatusLine().getStatusCode(); if (200 == statusCode) { result = getResponseString(httpResponse); } else { Log.e(LOG_TAG, "Connection failed: " + statusCode); } } catch (Exception e) { e.printStackTrace(); } finally { } return result; } public static String performPostRequest(String baseURL, String postData) { String result = ""; HttpResponse response = null; try { // URL使用基本URL即可,其中不需要加参数 HttpPost httpPost = new HttpPost(baseURL); // 设置ContentType httpPost.addHeader(HEADER_CONTENT_TYPE, getBodyContentType()); // 将请求体内容加入请求中 HttpEntity requestHttpEntity = prepareHttpEntity(postData); if (null != requestHttpEntity) { httpPost.setEntity(requestHttpEntity); } // 需要客户端对象来发送请求 HttpClient httpClient = new DefaultHttpClient(); // 发送请求 response = httpClient.execute(httpPost); final int statusCode = response.getStatusLine().getStatusCode(); if (200 == statusCode) { // 显示响应 result = getResponseString(response); } else { Log.e(LOG_TAG, "Connection failed: " + statusCode); } } catch (Exception e) { e.printStackTrace(); } finally { } return result; } /** * 直接利用String生成HttpEntity,String应该已经是key=value&key2=value2的形式 * * @param postData * @return */ private static HttpEntity prepareHttpEntity(String postData) { HttpEntity requestHttpEntity = null; try { if (null != postData) { // 去掉所有的换行 postData = postData.replace("\n", ""); // one way // requestHttpEntity = new ByteArrayEntity( // postData.getBytes(getParamsEncoding())); // another way requestHttpEntity = new StringEntity(postData, getParamsEncoding()); ((StringEntity) requestHttpEntity) .setContentEncoding(getParamsEncoding()); ((StringEntity) requestHttpEntity) .setContentType(getBodyContentType()); } } catch (Exception e) { e.printStackTrace(); } return requestHttpEntity; } /** * 利用Map结构的参数生成HttpEntity,使用UrlEncodedFormEntity对参数对进行编码 * * @param params * @return */ private static HttpEntity prepareHttpEntity1(Map- The string encoding used when converting parameter names and values * into bytes prior to URL encoding them.
*- The string encoding used when converting the URL encoded parameters * into a raw byte array.
*params) { // 需要将String里面的key value拆分出来 HttpEntity requestHttpEntity = null; try { if (null != params) { List pairList = new ArrayList ( params.size()); for (Map.Entry entry : params.entrySet()) { NameValuePair pair = new BasicNameValuePair(entry.getKey(), entry.getValue()); pairList.add(pair); } requestHttpEntity = new UrlEncodedFormEntity(pairList, getParamsEncoding()); } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return requestHttpEntity; } /** * 利用Map结构的参数生成HttpEntity,使用自己的方法对参数进行编码合成字符串 * * @param params * @return */ private static HttpEntity prepareHttpEntity2(Map params) { // 需要将String里面的key value拆分出来 HttpEntity requestHttpEntity = null; byte[] body = encodeParameters(params, getParamsEncoding()); requestHttpEntity = new ByteArrayEntity(body); return requestHttpEntity; } /** * Converts params
into an application/x-www-form-urlencoded * encoded string. */ private static byte[] encodeParameters(Mapparams, String paramsEncoding) { StringBuilder encodedParams = new StringBuilder(); try { for (Map.Entry entry : params.entrySet()) { encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding)); encodedParams.append('='); encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding)); encodedParams.append('&'); } return encodedParams.toString().getBytes(paramsEncoding); } catch (UnsupportedEncodingException uee) { throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee); } } public static String getResponseString(HttpResponse response) { String result = null; if (null == response) { return result; } HttpEntity httpEntity = response.getEntity(); InputStream inputStream = null; try { inputStream = httpEntity.getContent(); BufferedReader reader = new BufferedReader(new InputStreamReader( inputStream)); result = ""; String line = ""; while (null != (line = reader.readLine())) { result += line; } } catch (Exception e) { e.printStackTrace(); } finally { try { if (null != inputStream) { inputStream.close(); } } catch (IOException e) { e.printStackTrace(); } } return result; }}
参考资料
本博客HTTP标签下相关文章,比如这个:
Apache HttpClient:
Android之网络编程 系列博文:
Http Header详解:
Android--Apache HttpClient:
推荐项目
Android网络通信框架Volley:
Android Asynchronous Http Client:A Callback-Based Http Client Library for Android
也即:
本文项目地址(目前还是个挺简陋的Demo,有待完善):