转至 http://blog.csdn.net/liuhe688/article/details/6425225

在Android中,除了使用java.net包下的API访问HTTP服务之外,我们还可以换一种途径去完成工作。Android SDK附带了Apache的HttpClient API。Apache HttpClient是一个完善的HTTP客户端,它提供了对HTTP协议的全面支持,可以使用HTTP GET和POST进行访问。下面我们就结合实例,介绍一下HttpClient的使用方法。

我们新建一个http项目,项目结构如图:

Android中使用HTTP服务

在这个项目中,我们不需要任何的Activity,所有的操作都在单元测试类HttpTest.java中完成。

因为使用到了单元测试,所以在这里先介绍一下如何配置Android中的单元测试。所有配置信息均在AndroidManifest.xml中完成:

[xhtml] view plain copy
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <manifestxmlns:android="http://schemas.android.com/apk/res/android"
  3. package="com.scott.http"
  4. android:versionCode="1"
  5. android:versionName="1.0">
  6. <applicationandroid:icon="@drawable/icon"android:label="@string/app_name">
  7. <!--配置测试要使用的类库-->
  8. <uses-libraryandroid:name="android.test.runner"/>
  9. </application>
  10. <!--配置测试设备的主类和目标包-->
  11. <instrumentationandroid:name="android.test.InstrumentationTestRunner"
  12. android:targetPackage="com.scott.http"/>
  13. <!--访问HTTP服务所需的网络权限-->
  14. <uses-permissionandroid:name="android.permission.INTERNET"/>
  15. <uses-sdkandroid:minSdkVersion="8"/>
  16. </manifest>

然后,我们的单元测试类需要继承android.test.AndroidTestCase类,这个类本身是继承junit.framework.TestCase,并提供了getContext()方法,用于获取Android上下文环境,这个设计非常有用,因为很多Android API都是需要Context才能完成的。

现在让我们来看一下我们的测试用例,HttpTest.java代码如下:

[java] view plain copy
  1. packagecom.scot.http.test;
  2. importjava.io.ByteArrayOutputStream;
  3. importjava.io.InputStream;
  4. importjava.util.ArrayList;
  5. importjava.util.List;
  6. importjunit.framework.Assert;
  7. importorg.apache.http.HttpEntity;
  8. importorg.apache.http.HttpResponse;
  9. importorg.apache.http.HttpStatus;
  10. importorg.apache.http.NameValuePair;
  11. importorg.apache.http.client.HttpClient;
  12. importorg.apache.http.client.entity.UrlEncodedFormEntity;
  13. importorg.apache.http.client.methods.HttpGet;
  14. importorg.apache.http.client.methods.HttpPost;
  15. importorg.apache.http.entity.mime.MultipartEntity;
  16. importorg.apache.http.entity.mime.content.InputStreamBody;
  17. importorg.apache.http.entity.mime.content.StringBody;
  18. importorg.apache.http.impl.client.DefaultHttpClient;
  19. importorg.apache.http.message.BasicNameValuePair;
  20. importandroid.test.AndroidTestCase;
  21. publicclassHttpTestextendsAndroidTestCase{
  22. privatestaticfinalStringPATH="http://192.168.1.57:8080/web";
  23. publicvoidtestGet()throwsException{
  24. HttpClientclient=newDefaultHttpClient();
  25. HttpGetget=newHttpGet(PATH+"/TestServlet?id=1001&name=john&age=60");
  26. HttpResponseresponse=client.execute(get);
  27. if(response.getStatusLine().getStatusCode()==HttpStatus.SC_OK){
  28. InputStreamis=response.getEntity().getContent();
  29. Stringresult=inStream2String(is);
  30. Assert.assertEquals(result,"GET_SUCCESS");
  31. }
  32. }
  33. publicvoidtestPost()throwsException{
  34. HttpClientclient=newDefaultHttpClient();
  35. HttpPostpost=newHttpPost(PATH+"/TestServlet");
  36. List<NameValuePair>params=newArrayList<NameValuePair>();
  37. params.add(newBasicNameValuePair("id","1001"));
  38. params.add(newBasicNameValuePair("name","john"));
  39. params.add(newBasicNameValuePair("age","60"));
  40. HttpEntityformEntity=newUrlEncodedFormEntity(params);
  41. post.setEntity(formEntity);
  42. HttpResponseresponse=client.execute(post);
  43. if(response.getStatusLine().getStatusCode()==HttpStatus.SC_OK){
  44. InputStreamis=response.getEntity().getContent();
  45. Stringresult=inStream2String(is);
  46. Assert.assertEquals(result,"POST_SUCCESS");
  47. }
  48. }
  49. publicvoidtestUpload()throwsException{
  50. InputStreamis=getContext().getAssets().open("books.xml");
  51. HttpClientclient=newDefaultHttpClient();
  52. HttpPostpost=newHttpPost(PATH+"/UploadServlet");
  53. InputStreamBodyisb=newInputStreamBody(is,"books.xml");
  54. MultipartEntitymultipartEntity=newMultipartEntity();
  55. multipartEntity.addPart("file",isb);
  56. multipartEntity.addPart("desc",newStringBody("thisisdescription."));
  57. post.setEntity(multipartEntity);
  58. HttpResponseresponse=client.execute(post);
  59. if(response.getStatusLine().getStatusCode()==HttpStatus.SC_OK){
  60. is=response.getEntity().getContent();
  61. Stringresult=inStream2String(is);
  62. Assert.assertEquals(result,"UPLOAD_SUCCESS");
  63. }
  64. }
  65. //将输入流转换成字符串
  66. privateStringinStream2String(InputStreamis)throwsException{
  67. ByteArrayOutputStreambaos=newByteArrayOutputStream();
  68. byte[]buf=newbyte[1024];
  69. intlen=-1;
  70. while((len=is.read(buf))!=-1){
  71. baos.write(buf,0,len);
  72. }
  73. returnnewString(baos.toByteArray());
  74. }
  75. }

因为此文件包含三个测试用例,所以我将会逐个介绍一下。

首先,需要注意的是,我们定位服务器地址时使用到了IP,因为这里不能用localhost,服务端是在windows上运行,而本单元测试运行在Android平台,如果使用localhost就意味着在Android内部去访问服务,可能是访问不到的,所以必须用IP来定位服务。

我们先来分析一下testGet测试用例。我们使用了HttpGet,请求参数直接附在URL后面,然后由HttpClient执行GET请求,如果响应成功的话,取得响应内如输入流,并转换成字符串,最后判断是否为GET_SUCCESS。

testGet测试对应服务端Servlet代码如下:

[java] view plain copy
  1. @Override
  2. protectedvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{
  3. System.out.println("doGetmethodiscalled.");
  4. Stringid=request.getParameter("id");
  5. Stringname=request.getParameter("name");
  6. Stringage=request.getParameter("age");
  7. System.out.println("id:"+id+",name:"+name+",age:"+age);
  8. response.getWriter().write("GET_SUCCESS");
  9. }

然后再说testPost测试用例。我们使用了HttpPost,URL后面并没有附带参数信息,参数信息被包装成一个由NameValuePair类型组成的集合的形式,然后经过UrlEncodedFormEntity处理后调用HttpPost的setEntity方法进行参数设置,最后由HttpClient执行。

testPost测试对应的服务端代码如下:

[java] view plain copy
  1. @Override
  2. protectedvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{
  3. System.out.println("doPostmethodiscalled.");
  4. Stringid=request.getParameter("id");
  5. Stringname=request.getParameter("name");
  6. Stringage=request.getParameter("age");
  7. System.out.println("id:"+id+",name:"+name+",age:"+age);
  8. response.getWriter().write("POST_SUCCESS");
  9. }

上面两个是最基本的GET请求和POST请求,参数都是文本数据类型,能满足普通的需求,不过在有的场合例如我们要用到上传文件的时候,就不能使用基本的GET请求和POST请求了,我们要使用多部件的POST请求。下面介绍一下如何使用多部件POST操作上传一个文件到服务端。

由于Android附带的HttpClient版本暂不支持多部件POST请求,所以我们需要用到一个HttpMime开源项目,该组件是专门处理与MIME类型有关的操作。因为HttpMime是包含在HttpComponents 项目中的,所以我们需要去apache官方网站下载HttpComponents,然后把其中的HttpMime.jar包放到项目中去,如图:

Android中使用HTTP服务

然后,我们观察testUpload测试用例,我们用HttpMime提供的InputStreamBody处理文件流参数,用StringBody处理普通文本参数,最后把所有类型参数都加入到一个MultipartEntity的实例中,并将这个multipartEntity设置为此次POST请求的参数实体,然后执行POST请求。服务端Servlet代码如下:

[java] view plain copy
  1. packagecom.scott.web.servlet;
  2. importjava.io.FileOutputStream;
  3. importjava.io.IOException;
  4. importjava.util.Iterator;
  5. importjava.util.List;
  6. importjavax.servlet.ServletException;
  7. importjavax.servlet.http.HttpServlet;
  8. importjavax.servlet.http.HttpServletRequest;
  9. importjavax.servlet.http.HttpServletResponse;
  10. importorg.apache.commons.fileupload.FileItem;
  11. importorg.apache.commons.fileupload.FileItemFactory;
  12. importorg.apache.commons.fileupload.FileUploadException;
  13. importorg.apache.commons.fileupload.disk.DiskFileItemFactory;
  14. importorg.apache.commons.fileupload.servlet.ServletFileUpload;
  15. @SuppressWarnings("serial")
  16. publicclassUploadServletextendsHttpServlet{
  17. @Override
  18. @SuppressWarnings("rawtypes")
  19. protectedvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{
  20. booleanisMultipart=ServletFileUpload.isMultipartContent(request);
  21. if(isMultipart){
  22. FileItemFactoryfactory=newDiskFileItemFactory();
  23. ServletFileUploadupload=newServletFileUpload(factory);
  24. try{
  25. Listitems=upload.parseRequest(request);
  26. Iteratoriter=items.iterator();
  27. while(iter.hasNext()){
  28. FileItemitem=(FileItem)iter.next();
  29. if(item.isFormField()){
  30. //普通文本信息处理
  31. StringparamName=item.getFieldName();
  32. StringparamValue=item.getString();
  33. System.out.println(paramName+":"+paramValue);
  34. }else{
  35. //上传文件信息处理
  36. StringfileName=item.getName();
  37. byte[]data=item.get();
  38. StringfilePath=getServletContext().getRealPath("/files")+"/"+fileName;
  39. FileOutputStreamfos=newFileOutputStream(filePath);
  40. fos.write(data);
  41. fos.close();
  42. }
  43. }
  44. }catch(FileUploadExceptione){
  45. e.printStackTrace();
  46. }
  47. }
  48. response.getWriter().write("UPLOAD_SUCCESS");
  49. }
  50. }

服务端使用apache开源项目FileUpload进行处理,所以我们需要commons-fileupload和commons-io这两个项目的jar包,对服务端开发不太熟悉的朋友可以到网上查找一下相关资料。

介绍完上面的三种不同的情况之后,我们需要考虑一个问题,在实际应用中,我们不能每次都新建HttpClient,而是应该只为整个应用创建一个HttpClient,并将其用于所有HTTP通信。此外,还应该注意在通过一个HttpClient同时发出多个请求时可能发生的多线程问题。针对这两个问题,我们需要改进一下我们的项目:

1.扩展系统默认的Application,并应用在项目中。

2.使用HttpClient类库提供的ThreadSafeClientManager来创建和管理HttpClient。

改进后的项目结构如图:

Android中使用HTTP服务

其中MyApplication扩展了系统的Application,代码如下:

[java] view plain copy
  1. packagecom.scott.http;
  2. importorg.apache.http.HttpVersion;
  3. importorg.apache.http.client.HttpClient;
  4. importorg.apache.http.conn.ClientConnectionManager;
  5. importorg.apache.http.conn.scheme.PlainSocketFactory;
  6. importorg.apache.http.conn.scheme.Scheme;
  7. importorg.apache.http.conn.scheme.SchemeRegistry;
  8. importorg.apache.http.conn.ssl.SSLSocketFactory;
  9. importorg.apache.http.impl.client.DefaultHttpClient;
  10. importorg.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
  11. importorg.apache.http.params.BasicHttpParams;
  12. importorg.apache.http.params.HttpParams;
  13. importorg.apache.http.params.HttpProtocolParams;
  14. importorg.apache.http.protocol.HTTP;
  15. importandroid.app.Application;
  16. publicclassMyApplicationextendsApplication{
  17. privateHttpClienthttpClient;
  18. @Override
  19. publicvoidonCreate(){
  20. super.onCreate();
  21. httpClient=this.createHttpClient();
  22. }
  23. @Override
  24. publicvoidonLowMemory(){
  25. super.onLowMemory();
  26. this.shutdownHttpClient();
  27. }
  28. @Override
  29. publicvoidonTerminate(){
  30. super.onTerminate();
  31. this.shutdownHttpClient();
  32. }
  33. //创建HttpClient实例
  34. privateHttpClientcreateHttpClient(){
  35. HttpParamsparams=newBasicHttpParams();
  36. HttpProtocolParams.setVersion(params,HttpVersion.HTTP_1_1);
  37. HttpProtocolParams.setContentCharset(params,HTTP.DEFAULT_CONTENT_CHARSET);
  38. HttpProtocolParams.setUseExpectContinue(params,true);
  39. SchemeRegistryschReg=newSchemeRegistry();
  40. schReg.register(newScheme("http",PlainSocketFactory.getSocketFactory(),80));
  41. schReg.register(newScheme("https",SSLSocketFactory.getSocketFactory(),443));
  42. ClientConnectionManagerconnMgr=newThreadSafeClientConnManager(params,schReg);
  43. returnnewDefaultHttpClient(connMgr,params);
  44. }
  45. //关闭连接管理器并释放资源
  46. privatevoidshutdownHttpClient(){
  47. if(httpClient!=null&&httpClient.getConnectionManager()!=null){
  48. httpClient.getConnectionManager().shutdown();
  49. }
  50. }
  51. //对外提供HttpClient实例
  52. publicHttpClientgetHttpClient(){
  53. returnhttpClient;
  54. }
  55. }

我们重写了onCreate()方法,在系统启动时就创建一个HttpClient;重写了onLowMemory()和onTerminate()方法,在内存不足和应用结束时关闭连接,释放资源。需要注意的是,当实例化DefaultHttpClient时,传入一个由ThreadSafeClientConnManager创建的一个ClientConnectionManager实例,负责管理HttpClient的HTTP连接。

然后,想要让我们这个加强版的“Application”生效,需要在AndroidManifest.xml中做如下配置:

[c-sharp] view plain copy
  1. <applicationandroid:name=".MyApplication"...>
  2. ...
  3. </application>

如果我们没有配置,系统默认会使用android.app.Application,我们添加了配置,系统就会使用我们的com.scott.http.MyApplication,然后就可以在context中调用getApplication()来获取MyApplication实例。

有了上面的配置,我们就可以在活动中应用了,HttpActivity.java代码如下:

[c-sharp] view plain copy
  1. packagecom.scott.http;
  2. importjava.io.ByteArrayOutputStream;
  3. importjava.io.InputStream;
  4. importorg.apache.http.HttpResponse;
  5. importorg.apache.http.HttpStatus;
  6. importorg.apache.http.client.HttpClient;
  7. importorg.apache.http.client.methods.HttpGet;
  8. importandroid.app.Activity;
  9. importandroid.os.Bundle;
  10. importandroid.view.View;
  11. importandroid.widget.Button;
  12. importandroid.widget.Toast;
  13. publicclassHttpActivityextendsActivity{
  14. @Override
  15. protectedvoidonCreate(BundlesavedInstanceState){
  16. super.onCreate(savedInstanceState);
  17. setContentView(R.layout.main);
  18. Buttonbtn=(Button)findViewById(R.id.btn);
  19. btn.setOnClickListener(newView.OnClickListener(){
  20. @Override
  21. publicvoidonClick(Viewv){
  22. execute();
  23. }
  24. });
  25. }
  26. privatevoidexecute(){
  27. try{
  28. MyApplicationapp=(MyApplication)this.getApplication();//获取MyApplication实例
  29. HttpClientclient=app.getHttpClient();//获取HttpClient实例
  30. HttpGetget=newHttpGet("http://192.168.1.57:8080/web/TestServlet?id=1001&name=john&age=60");
  31. HttpResponseresponse=client.execute(get);
  32. if(response.getStatusLine().getStatusCode()==HttpStatus.SC_OK){
  33. InputStreamis=response.getEntity().getContent();
  34. Stringresult=inStream2String(is);
  35. Toast.makeText(this,result,Toast.LENGTH_LONG).show();
  36. }
  37. }catch(Exceptione){
  38. e.printStackTrace();
  39. }
  40. }
  41. //将输入流转换成字符串
  42. privateStringinStream2String(InputStreamis)throwsException{
  43. ByteArrayOutputStreambaos=newByteArrayOutputStream();
  44. byte[]buf=newbyte[1024];
  45. intlen=-1;
  46. while((len=is.read(buf))!=-1){
  47. baos.write(buf,0,len);
  48. }
  49. returnnewString(baos.toByteArray());
  50. }
  51. }

点击“execute”按钮,执行结果如下:

Android中使用HTTP服务

更多相关文章

  1. Android Hawk数据库 github开源项目
  2. [置顶] android studio导入项目后出现cannot resolve symbol r a
  3. Android传感器使用实例1
  4. Android CTS 4.03测试总结
  5. 比较实用的开源项目总结
  6. 完成第一个android APP的UI项目

随机推荐

  1. sqlserver 模糊查询常用方法
  2. sql根据表名获取字段及对应说明
  3. SQL语句优化方法30例(推荐)
  4. 关于存储过程的编写的一些体会
  5. SQLSERVER 表分区操作和设计方法
  6. ASP.NET下向SQLServer2008导入文件实例操
  7. sqlserver中distinct的用法(不重复的记录
  8. 用sql脚本创建sqlserver数据库触发器范例
  9. 用sql脚本创建sqlserver数据库范例语句
  10. sqlserver中比较一个字符串中是否含含另