Java Android HTTP实现总结

  Http(Hypertext Transfer Protocol)超文本传输协议,是一个基于请求/响应模式的无状态的协议,Http1.1版给出了持续连接的机制,客户端建立连接之后,可以发送多次请求,当不会再发送时再关闭连接。

  Android使用Java,对于Http协议的基本功能有两种实现方案:

  1.使用JDK的java.net包下的HttpURLConnection.

  2.使用Apache的HttpClient

  关于二者的比较可以看一下:

  http://www.cnblogs.com/devinzhang/archive/2012/01/17/2325092.html

  Android SDK中集成了Apache的HttpClient模块,也即说Android上两种方法都能用。

  之前看一个Android开发者博客(原文链接先空缺,需要翻墙)对此的讨论,大意总结如下:

  1.HttpClient的功能比较全,更加强大;而HttpURLConnection的功能较简单和原始,但是性能更好。

  2.在Android 2.x的版本中使用HttpURLConnection有bug,但是后来高级版本的Android已经将带来的bug修复,并且做了一些进一步优化的工作,所以建议在高级版本的Android系统(Android 2.3之后)使用HttpURLConnection,低版本的系统仍使用HttpClient。

程序实现

  下面来讨论一下实现,首先,需要确认Manifest中有权限:

    <uses-permission android:name="android.permission.INTERNET" />

使用JDK的HttpURLConnection类

  HttpURLConnection参考:

  http://developer.android.com/reference/java/net/HttpURLConnection.html

  使用这个类的一般步骤:

  1.通过URL.openConnection() 方法获取一个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)。如果有请求体那么setDoOutput(true)必须为true,然后把输入放在getOutputStream()流中。

  4.读取响应。响应的headers一般包括了一些metadata比如响应体的内容类型和长度,修改日期以及session cookies。响应体可以从 getInputStream()流中读出。

  5.断开连接。响应体被读出之后,应该调用 disconnect()方法来断开连接。

  例子代码:

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,
Map<String, String> params) {
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<String, String> params) {
byte[] data = null; try {
if (null != params && !params.isEmpty()) {
StringBuffer buffer = new StringBuffer(); for (Map.Entry<String, String> 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;
}
}

HttpUtilsJDK

使用Apache的HttpClient

  可以查看官方的Tutorial:

  http://hc.apache.org/httpcomponents-client-ga/tutorial/html/index.html

  Android有一个实现类AndroidHttpClient,实现了HttpClient

  http://developer.android.com/reference/android/net/http/AndroidHttpClient.html

  包装了一些默认的设置。

关于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这个类是用来把输入数据编码成合适的内容,比如下面这段:

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("http://localhost/handler.do");
httppost.setEntity(entity);

  两个键值对,被UrlEncodedFormEntity实例编码后变为如下内容:

param1=value1&param2=value2

  

  使用Apache的HttpClient发送HTTP请求的辅助类,例子代码:

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.
*
* <p>
* This controls both encodings:
* <ol>
* <li>The string encoding used when converting parameter names and values
* into bytes prior to URL encoding them.</li>
* <li>The string encoding used when converting the URL encoded parameters
* into a raw byte array.</li>
* </ol>
*/
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<String, String> params) {
// 需要将String里面的key value拆分出来 HttpEntity requestHttpEntity = null;
try { if (null != params) {
List<NameValuePair> pairList = new ArrayList<NameValuePair>(
params.size());
for (Map.Entry<String, String> 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<String, String> params) {
// 需要将String里面的key value拆分出来 HttpEntity requestHttpEntity = null;
byte[] body = encodeParameters(params, getParamsEncoding());
requestHttpEntity = new ByteArrayEntity(body); return requestHttpEntity;
} /**
* Converts <code>params</code> into an application/x-www-form-urlencoded
* encoded string.
*/
private static byte[] encodeParameters(Map<String, String> params,
String paramsEncoding) {
StringBuilder encodedParams = new StringBuilder();
try {
for (Map.Entry<String, String> 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; }
}

HttpUtilsApache

参考资料

  本博客HTTP标签下相关文章,比如这个:

  http://www.cnblogs.com/mengdd/p/3144599.html

  Apache HttpClient:

  http://hc.apache.org/httpcomponents-client-ga/

  http://hc.apache.org/httpcomponents-client-ga/tutorial/html/fundamentals.html#d5e49

  Android之网络编程 系列博文:

  http://www.cnblogs.com/devinzhang/category/349642.html

  Http Header详解:

  http://kb.cnblogs.com/page/92320/

  Android--Apache HttpClient:

  http://www.cnblogs.com/plokmju/p/Android_apacheHttpClient.html

推荐项目

  Android网络通信框架Volley:

  https://github.com/mengdd/android-volley

  Android Asynchronous Http Client:A Callback-Based Http Client Library for Android

  https://github.com/mengdd/android-async-http

  也即:http://loopj.com/android-async-http/

  本文项目地址(目前还是个挺简陋的Demo,有待完善):

  https://github.com/mengdd/HelloAndroidHttpUtils

Java Android HTTP实现总结的更多相关文章

  1. Java/Android引用类型及其使用分析

    Java/Android中有四种引用类型,分别是: Strong reference     - 强引用Soft Reference        - 软引用Weak Reference      - ...

  2. Atitti.java android反编译解决方案-----虚拟机方案

    Atitti.java android反编译解决方案-----虚拟机方案 哈哈,终极解决方案是虚拟机...c++也可以反编译为汇编代码,但无需担心,因为读懂汇编太麻烦..只要不能拿到c++源码就可.. ...

  3. Java Android 注解(Annotation) 及几个常用开源项目注解原理简析

    不少开源库(ButterKnife.Retrofit.ActiveAndroid等等)都用到了注解的方式来简化代码提高开发效率. 本文简单介绍下 Annotation 示例.概念及作用.分类.自定义. ...

  4. 性能优化之Java(Android)代码优化

    最新最准确内容建议直接访问原文:性能优化之Java(Android)代码优化 本文为Android性能优化的第三篇——Java(Android)代码优化.主要介绍Java代码中性能优化方式及网络优化, ...

  5. [Java][Android][Process] 暴力的服务能够解决一切,暴力的方式运行命令行语句

    不管是在Java或者Android中运行命令行语句殊途同归都是创建一个子进程运行调用可运行文件运行命令.类似于Windows中的CMD一样. 此时你有两种方式运行:ProcessBuilder与Run ...

  6. Xml解析之——Java/Android/Python

    Xml解析之——Java/Android/Python 一.Xml文件 test.xml <note> <to>George</to> <from>Jo ...

  7. Java&Android反编工具打包

    Java&Android反编工具: 1.Eclipse反编插件:安装到Eclipse后,可以简要的查看jar包中的*.class; 2.DoAPK:反编*.apk为smali和一些资源文件,可 ...

  8. java android 环境变量配置备忘录

    java android 环境变量配置备忘录,全新重装系统要用到,备忘下来; JAVA_HOME D:\Java\jdk1.6.0_07 CLASSPATH  .;%JAVA_HOME%\lib\dt ...

  9. Atitit.嵌入式web 服务器 java android最佳实践

    Atitit.嵌入式web 服务器 java android最佳实践 1. Android4.4.21 2. 自己的webserver1 3. CyberHTTP for Java  cybergar ...

随机推荐

  1. [git]撤销的相关命令:reset、revert、checkout

    基本概念 工作区 暂存区 本地版本仓库 远程版本仓库 如果不清晰上面的四个概念,请查看廖老师的git教程 这里我多说几句:最开始我使用git的时候,我并不明白我为什么写完代码要用git的一些列指令把我 ...

  2. springmvc学习笔记--mybatis--使用插件自动生成实体和mapper

    由于表对象在开发过程中会增删字段,有时候需要重新生成实体和对应的mapper,这时候可以通过mybatis的插件的生成. 优点是快速简洁,缺点同样很明显:覆盖.因此,通常是在第一次搭建框架的时候使用, ...

  3. placeholder的兼容处理(jQuery下)

    这是一个老问题,结合前辈们的经验,需要处理的问题有一下几个. 1.只有输入框(input/textarea)下的palaceholder属性存在的时候才需要处理这类兼容 2.处理好输入框上焦点和是焦点 ...

  4. 实现iOS图片等资源文件的热更新化(一): 从Images.xcassets导出合适的图片

    本文会基于一个已有的脚本工具自动导出所有的图片;最终给出的是一个从 Images.xcassets 到基于文件夹的精简 合适 的图片资源集的完整过程.难点在于从完整图片集到精简图片集,肯定是基于一个定 ...

  5. 解析导航栏的url--selnium,beautifulsoup实战

    前段时间做ui自动化测试的时候,导航栏菜单始终有点问题,最后只好直接获取到url,然后直接使用driver.get(url)进入页面: 包括做压测的时候,比如我要找出所有报表菜单的url,这样不可能手 ...

  6. 慎用Assembly.LoadFile()和Assembly.LoadFrom()

    经测这俩方法会锁住文件,导致程序运行期间无法对load过的程序集文件进行更名/删除/覆盖等等操作,考虑用Assembly.Load()文件字节组替代: Assembly.Load(File.ReadA ...

  7. 在使用vs2012中的MVC4出的错误!类型“System.Data.Entity.DbContext”在未被引用的程序集中定义

    理解决方案的nuget程序包2.选中已安装的包,找到EntityFramework,点击管理 由于我是在Taobao_DAL中用到了EF的“数据上下文对象”,他报错了.所以我们要选中它,将EF5.0安 ...

  8. Java总结篇系列:Java多线程(二)

    本文承接上一篇文章<Java总结篇系列:Java多线程(一)>. 四.Java多线程的阻塞状态与线程控制 上文已经提到Java阻塞的几种具体类型.下面分别看下引起Java线程阻塞的主要方法 ...

  9. FAILED BINDER TRANSACTION

    最近开发Widget,要在Widget上显示图片,出现了FAILED BINDER TRANSACTION错误,后来才发现是因为图片太大了.Widget使用的是RemoteViews,而Intent传 ...

  10. MVC-自定义过滤器

    APS.NET MVC中的每一个请求,都会分配给相应的控制器和对应的行为方法去处理,而在这些处理的前前后后如果想再加一些额外的逻辑处理.这时候就用到了过滤器. 自定义过滤器: 若要自定义一个过滤器,则 ...