前言

  上一篇文章介绍了使用HttpURLConnection来完成对于HTTP协议的支持,现在介绍一个新的方式来访问Web站点,那就是HttpClient。

  HttpClient是Apache开源组织提供的一个开源的项目,从名字上就可以看出,它是一个简单的HTTP客户端(并不是浏览器),可以发送HTTP请求,接受HTTP响应。但是不会缓存服务器的响应,不能执行HTTP页面中签入嵌入的JS代码,自然也不会对页面内容进行任何解析、处理,这些都是需要开发人员来完成的。

  现在Android已经成功集成了HttpClient,所以开发人员在Android项目中可以直接使用HttpClient来想Web站点提交请求以及接受响应,如果使用其他的Java项目,需要引入进相应的Jar包。HttpClient可以在官网上下载

  

HttpClient

  HttpClient其实是一个interface类型,HttpClient封装了对象需要执行的Http请求、身份验证、连接管理和其它特性。从文档上看,HttpClient有三个已知的实现类分别是:AbstractHttpClient, AndroidHttpClient, DefaultHttpClient,会发现有一个专门为Android应用准备的实现类AndroidHttpClient,当然使用常规的DefaultHttpClient也可以实现功能,但是既然开发的是Android应用程序,还是使用Android专有的实现类,一定有其优势。

  从两个类包所有在位置就可以看出区别,AndroidHttpClient定义在android.net.http.AndroidHttpClient包下,属于Android原生的http访问,而DefaultHttpClient定义在org.apache.http.impl.client.DefaultHttpClient包下,属于对apche项目的支持。而AndroidHttpClient没有公开的构造函数,只能通过静态方法newInstance()方法来获得AndroidHttpClient对象。

  AndroidHttpClient对于DefaultHttpClient做了一些改进,使其更使用用于Android项目:

  1. 关掉过期检查,自连接可以打破所有的时间限制。
  2. 可以设置ConnectionTimeOut(连接超时)和SoTimeout(读取数据超时)。
  3. 关掉重定向。
  4. 使用一个Session缓冲用于SSL Sockets。
  5. 如果服务器支持,使用gzip压缩方式用于在服务端和客户端传递的数据。
  6. 默认情况下不保留Cookie。    

  简单来说,用HttpClient发送请求、接收响应都很简单,只需要几个步骤即可:

  1. 创建HttpClient对象。
  2. 创建对应的发送请求的对象,如果需要发送GET请求,则创建HttpGet对象,如果需要发送POST请求,则创建HttpPost对象。
  3. 对于发送请求的参数,GET和POST使用的方式不同,GET方式可以使用拼接字符串的方式,把参数拼接在URL结尾;POST方式需要使用setEntity(HttpEntity entity)方法来设置请求参数。
  4. 调用HttpClient对象的execute(HttpUriRequest request)发送请求,执行该方法返回一个HttpResponse对象。
  5. 调用HttpResponse的对应方法获取服务器的响应头、响应内容等。

DefaultHttpClient

  先看看使用DefaultHttpClient方式发送Web站点请求,上面已经简要说明了步骤,在这里简要说明一个参数的传递问题,对于GET方式,只需要拼接字符串就在URL结尾即可,但是对于POST方式,需要传递HttpEntity对象,HttpEntity为一个接口,有多个实现类,可以使用其间接子继承,UrlEncodedFormEntity类来保存请求参数,并传递给HttpPost。

  此例子简单实现了在Android客户端使用DefaultHttpClient实现一个Http站点登陆的实现,使用的是POST传递,其传递值只需要传递username+password即可,当传递的数据为admin+123则认为登陆成功。Web站点使用.net的架构,一个一般处理程序,简单的比对账户密码,这里就不在此讲解。

  因为Android4.0之后对使用网络有特殊要求,已经无法再在主线程中访问网络了,必须使用多线程访问的模式,其他的一些信息在代码注释中已经说明。

 DefaultHttpClient-Code

复制代码
  1. 1 package com.bgxt.httpUtils;
  2. 2
  3. 3 import java.io.ByteArrayOutputStream;
  4. 4 import java.io.IOException;
  5. 5 import java.io.InputStream;
  6. 6 import java.io.UnsupportedEncodingException;
  7. 7 import java.util.ArrayList;
  8. 8 import java.util.HashMap;
  9. 9 import java.util.List;
  10. 10 import java.util.Map;
  11. 11
  12. 12 import org.apache.http.HttpResponse;
  13. 13 import org.apache.http.NameValuePair;
  14. 14 import org.apache.http.client.ClientProtocolException;
  15. 15 import org.apache.http.client.entity.UrlEncodedFormEntity;
  16. 16 import org.apache.http.client.methods.HttpPost;
  17. 17 import org.apache.http.impl.client.DefaultHttpClient;
  18. 18 import org.apache.http.message.BasicNameValuePair;
  19. 19
  20. 20 public class httpClientUtils implements Runnable {
  21. 21 /**
  22. 22 * 对于Android4.0之上的环境下,不能在主线程中访问网络 所以这里另新建了一个实现了Runnable接口的Http访问类
  23. 23 */
  24. 24 private String username;
  25. 25 private String password;
  26. 26
  27. 27 public httpClientUtils(String username, String password) {
  28. 28 // 初始化用户名和密码
  29. 29 this.username = username;
  30. 30 this.password = password;
  31. 31 }
  32. 32
  33. 33 @Override
  34. 34 public void run() {
  35. 35 // 设置访问的Web站点
  36. 36 String path = "http://192.168.1.103:1231/loginas.ashx";
  37. 37 // 设置Http请求参数
  38. 38 Map<String, String> params = new HashMap<String, String>();
  39. 39 params.put("username", username);
  40. 40 params.put("password", password);
  41. 41
  42. 42 String result = sendHttpClientPost(path, params, "utf-8");
  43. 43 // 把返回的接口输出
  44. 44 System.out.println(result);
  45. 45 }
  46. 46
  47. 47 /**
  48. 48 * 发送Http请求到Web站点
  49. 49 *
  50. 50 * @param path
  51. 51 * Web站点请求地址
  52. 52 * @param map
  53. 53 * Http请求参数
  54. 54 * @param encode
  55. 55 * 编码格式
  56. 56 * @return Web站点响应的字符串
  57. 57 */
  58. 58 private String sendHttpClientPost(String path, Map<String, String> map,
  59. 59 String encode) {
  60. 60 List<NameValuePair> list = new ArrayList<NameValuePair>();
  61. 61 if (map != null && !map.isEmpty()) {
  62. 62 for (Map.Entry<String, String> entry : map.entrySet()) {
  63. 63 // 解析Map传递的参数,使用一个键值对对象BasicNameValuePair保存。
  64. 64 list.add(new BasicNameValuePair(entry.getKey(), entry
  65. 65 .getValue()));
  66. 66 }
  67. 67 }
  68. 68 try {
  69. 69 // 实现将请求 的参数封装封装到HttpEntity中。
  70. 70 UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, encode);
  71. 71 // 使用HttpPost请求方式
  72. 72 HttpPost httpPost = new HttpPost(path);
  73. 73 // 设置请求参数到Form中。
  74. 74 httpPost.setEntity(entity);
  75. 75 // 实例化一个默认的Http客户端
  76. 76 DefaultHttpClient client = new DefaultHttpClient();
  77. 77 // 执行请求,并获得响应数据
  78. 78 HttpResponse httpResponse = client.execute(httpPost);
  79. 79 // 判断是否请求成功,为200时表示成功,其他均问有问题。
  80. 80 if (httpResponse.getStatusLine().getStatusCode() == 200) {
  81. 81 // 通过HttpEntity获得响应流
  82. 82 InputStream inputStream = httpResponse.getEntity().getContent();
  83. 83 return changeInputStream(inputStream, encode);
  84. 84 }
  85. 85 } catch (UnsupportedEncodingException e) {
  86. 86 e.printStackTrace();
  87. 87 } catch (ClientProtocolException e) {
  88. 88 e.printStackTrace();
  89. 89 } catch (IOException e) {
  90. 90 e.printStackTrace();
  91. 91 }
  92. 92 return "";
  93. 93 }
  94. 94
  95. 95 /**
  96. 96 * 把Web站点返回的响应流转换为字符串格式
  97. 97 *
  98. 98 * @param inputStream
  99. 99 * 响应流
  100. 100 * @param encode
  101. 101 * 编码格式
  102. 102 * @return 转换后的字符串
  103. 103 */
  104. 104 private String changeInputStream(InputStream inputStream, String encode) {
  105. 105 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
  106. 106 byte[] data = new byte[1024];
  107. 107 int len = 0;
  108. 108 String result = "";
  109. 109 if (inputStream != null) {
  110. 110 try {
  111. 111 while ((len = inputStream.read(data)) != -1) {
  112. 112 outputStream.write(data, 0, len);
  113. 113 }
  114. 114 result = new String(outputStream.toByteArray(), encode);
  115. 115
  116. 116 } catch (IOException e) {
  117. 117 e.printStackTrace();
  118. 118 }
  119. 119 }
  120. 120 return result;
  121. 121 }
  122. 122
  123. 123 }
复制代码

AndroidHttpClient

  使用AndroidHttpClient的方式和DefaultHttpClient差不多,不多的几点区别上面已经说明,但是在此例子中没有体现。有一点需要注意的是,AndroidHttpClient是一个final类,也没有公开的构造函数,所以无法使用new的形式对其进行实例化,必须使用AndroidHttpClient.newInstance()方法获得AndroidHttpClient对象。

  示例中依然是使用POST请求,实现的功能和DefaultHttpClient示例一样。细节部分已经在注释中体现,直接看代码即可。

复制代码
  1. 1 package com.bgxt.httpUtils;
  2. 2
  3. 3 import java.io.ByteArrayOutputStream;
  4. 4 import java.io.IOException;
  5. 5 import java.io.InputStream;
  6. 6 import java.io.UnsupportedEncodingException;
  7. 7 import java.util.ArrayList;
  8. 8 import java.util.HashMap;
  9. 9 import java.util.List;
  10. 10 import java.util.Map;
  11. 11
  12. 12 import org.apache.http.HttpResponse;
  13. 13 import org.apache.http.NameValuePair;
  14. 14 import org.apache.http.client.ClientProtocolException;
  15. 15 import org.apache.http.client.HttpClient;
  16. 16 import org.apache.http.client.entity.UrlEncodedFormEntity;
  17. 17 import org.apache.http.client.methods.HttpPost;
  18. 18 import org.apache.http.impl.client.DefaultHttpClient;
  19. 19 import org.apache.http.message.BasicNameValuePair;
  20. 20
  21. 21 import android.net.http.AndroidHttpClient;
  22. 22
  23. 23 public class AndroidHttpClientUtils implements Runnable {
  24. 24
  25. 25 private String username;
  26. 26 private String password;
  27. 27
  28. 28 public AndroidHttpClientUtils(String username, String password) {
  29. 29 // 初始化用户名和密码
  30. 30 this.username = username;
  31. 31 this.password = password;
  32. 32 }
  33. 33
  34. 34 @Override
  35. 35 public void run() {
  36. 36 // 设置访问的Web站点
  37. 37 String path = "http://192.168.1.103:1231/loginas.ashx";
  38. 38 //设置Http请求参数
  39. 39 Map<String, String> params = new HashMap<String, String>();
  40. 40 params.put("username", username);
  41. 41 params.put("password", password);
  42. 42
  43. 43 String result = sendHttpClientPost(path, params, "utf-8");
  44. 44 //把返回的接口输出
  45. 45 System.out.println(result);
  46. 46 }
  47. 47 /**
  48. 48 * 发送Http请求到Web站点
  49. 49 * @param path Web站点请求地址
  50. 50 * @param map Http请求参数
  51. 51 * @param encode 编码格式
  52. 52 * @return Web站点响应的字符串
  53. 53 */
  54. 54 private String sendHttpClientPost(String path,Map<String, String> map,String encode)
  55. 55 {
  56. 56 List<NameValuePair> list=new ArrayList<NameValuePair>();
  57. 57 if(map!=null&&!map.isEmpty())
  58. 58 {
  59. 59 for(Map.Entry<String, String> entry:map.entrySet())
  60. 60 {
  61. 61 //解析Map传递的参数,使用一个键值对对象BasicNameValuePair保存。
  62. 62 list.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
  63. 63 }
  64. 64 }
  65. 65 try {
  66. 66 //实现将请求 的参数封装封装到HttpEntity中。
  67. 67 UrlEncodedFormEntity entity=new UrlEncodedFormEntity(list, encode);
  68. 68 //使用HttpPost请求方式
  69. 69 HttpPost httpPost=new HttpPost(path);
  70. 70 //设置请求参数到Form中。
  71. 71 httpPost.setEntity(entity);
  72. 72 //实例化一个默认的Http客户端,使用的是AndroidHttpClient
  73. 73 HttpClient client=AndroidHttpClient.newInstance("");
  74. 74 //执行请求,并获得响应数据
  75. 75 HttpResponse httpResponse= client.execute(httpPost);
  76. 76 //判断是否请求成功,为200时表示成功,其他均问有问题。
  77. 77 if(httpResponse.getStatusLine().getStatusCode()==200)
  78. 78 {
  79. 79 //通过HttpEntity获得响应流
  80. 80 InputStream inputStream=httpResponse.getEntity().getContent();
  81. 81 return changeInputStream(inputStream,encode);
  82. 82 }
  83. 83
  84. 84 } catch (UnsupportedEncodingException e) {
  85. 85 // TODO Auto-generated catch block
  86. 86 e.printStackTrace();
  87. 87 } catch (ClientProtocolException e) {
  88. 88 // TODO Auto-generated catch block
  89. 89 e.printStackTrace();
  90. 90 } catch (IOException e) {
  91. 91 // TODO Auto-generated catch block
  92. 92 e.printStackTrace();
  93. 93 }
  94. 94
  95. 95 return "";
  96. 96 }
  97. 97 /**
  98. 98 * 把Web站点返回的响应流转换为字符串格式
  99. 99 * @param inputStream 响应流
  100. 100 * @param encode 编码格式
  101. 101 * @return 转换后的字符串
  102. 102 */
  103. 103 private String changeInputStream(InputStream inputStream,
  104. 104 String encode) {
  105. 105 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
  106. 106 byte[] data = new byte[1024];
  107. 107 int len = 0;
  108. 108 String result="";
  109. 109 if (inputStream != null) {
  110. 110 try {
  111. 111 while ((len = inputStream.read(data)) != -1) {
  112. 112 outputStream.write(data,0,len);
  113. 113 }
  114. 114 result=new String(outputStream.toByteArray(),encode);
  115. 115
  116. 116 } catch (IOException e) {
  117. 117 e.printStackTrace();
  118. 118 }
  119. 119 }
  120. 120 return result;
  121. 121 }
  122. 122 }
复制代码

  在本文的示例中,环境是使用的Android项目,可以对其进行简单的界面布局,如图:

  如果输入用户和密码为:admin+123,则可以再LogCat中查看到登录成功。

  源码下载

 

总结

  最近的两次博客中,已经分别介绍了HttpUrlConnection和HttpClient两种方式,通过Http协议对Web站点的访问。如果还不了解HttpURLConnection的读者,可以看看Android–Http协议

  根据官方文档上说的显示,Android包括两个Http客户端:HttpURLConnection和Apache HttpClient。并且都支持HTTPS,流媒体上传下载,并且可配置超时以及支持IPv6和连接池技术。但是因为移动设备的局限性,HttpURLConnection会是比Apache Http更好的选择,因为其API简单,运行消耗内存小,并且具有公开化的压缩算法,以及响应缓存,能更好的减少网络使用,提供运行速度和节省电池。

  但是也不能否认Apache HttpClient,它有大量的灵活的API,实现比较稳定,少有Bug,可造成的问题就是很难在不影响其兼容性的情况下对其进行改进了。现在Android开发者已经慢慢放弃Apache HttpClient的使用,转而使用HttpURLConnection。但是对于Android2.2之前的版本,HttpURLConnection具有一个致命的BUG,在响应输入流InputStream中调用.Close()方法将会阻碍连接池,因为这个BUG,只能放弃连接池的使用,但是Apache HttpClient不存在这个问题,当然Android2.3之后的版本中,HttpURLConnection已经解决了这个BUG,可以放心使用。

 

版权声明:本文为gaoxiangde原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/gaoxiangde/p/4379847.html