昨天我们只对Android接收网络数据进行了简单介绍,今天我们完成了Android数据存储网络部分的所有内容。在此我将对这非常重要的内容进行总结。



本篇日志是对Android与WEB应用服务之间进行数据交互的总结,下篇日志是一个经典而又让人十分好奇的Android多线程断点下载应用的总结。下面我们开始Android与网络数据的交互。



一、创建WEB应用服务

使用eclipse3.5创建一个动态WEB应用,使用Struts1处理用户请求。我们此应用添加一个DispatchAction,并为它添加四个方法创建用于处理Android以各种方式提交的请求。



1.创建动态WEB工程

Project name:AndroidWebServer 


 Target runtime:Apache Tomcat v6.0 


 Dynamic web module version:2.5 


 Configuration:Default Configuration for Apache Tomcat v6.0 




 2.添加DispatchAction 


package com.changcheng.web.struts.actions; 




import java.io.File; 


import java.io.FileOutputStream; 


import javax.servlet.http.HttpServletRequest; 


import javax.servlet.http.HttpServletResponse; 


import org.apache.struts.action.ActionForm; 


import org.apache.struts.action.ActionForward; 


import org.apache.struts.action.ActionMapping; 


import org.apache.struts.actions.DispatchAction; 


import com.changcheng.web.struts.forms.DataForm; 




public class AndroidWebServer extends DispatchAction { 




 // Andoird以Get方式发送的请求 


 public ActionForward sendDataByGet(ActionMapping mapping, ActionForm form, 


 HttpServletRequest request, HttpServletResponse response) 


 throws Exception { 


 String name = request.getParameter("name"); 


 request.setAttribute("message", "Hello " + name); 


 return mapping.findForward("success"); 


 } 




 // Andoird以Post方式发送的请求 


 public ActionForward sendDataByPost(ActionMapping mapping, ActionForm form, 


 HttpServletRequest request, HttpServletResponse response) 


 throws Exception { 


 String name = request.getParameter("name"); 


 request.setAttribute("message", "Hello " + name); 


 return mapping.findForward("success"); 


 } 




 // Andoird以表单方式发送的请求 


 public ActionForward sendDataByForm(ActionMapping mapping, ActionForm form, 


 HttpServletRequest request, HttpServletResponse response) 


 throws Exception { 


 DataForm formbean = (DataForm) form; 


 System.out.println("StrData:" + formbean.getStrData()); 


 // 获取上传的文件 


 if (formbean.getFileData() != null 


 && formbean.getFileData().getFileSize() > 0) { 


 // 设置保存目录 


 File dir = new File(request.getSession().getServletContext() 


 .getRealPath("/images")); 


 if (!dir.exists()) 


 dir.mkdirs(); 


 // 保存文件 


 FileOutputStream outStream = new FileOutputStream(new File(dir, 


 formbean.getFileData().getFileName())); 


 outStream.write(formbean.getFileData().getFileData());// 保存文件 


 outStream.close(); 


 } 


 return null; 


 } 


} 




 3.向web.xml添加Struts1的ActionServlet 


<servlet> 


 <servlet-name>struts</servlet-name> 


 <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> 


 <init-param> 


 <param-name>config</param-name> 


 <param-value>/WEB-INF/struts-config.xml</param-value> 


 </init-param> 


</servlet> 


<servlet-mapping> 


 <servlet-name>struts</servlet-name> 


 <url-pattern>*.do</url-pattern> 


</servlet-mapping> 




 4.struts-config.xml 


<?xml version="1.0" encoding="UTF-8"?> 


<!DOCTYPE struts-config PUBLIC 


 "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN" 


 "http://struts.apache.org/dtds/struts-config_1_3.dtd"> 




<struts-config> 


 <form-beans> 


 <form-bean name="dataForm" type="com.changcheng.web.struts.forms.DataForm" /> 


 </form-beans> 


 <action-mappings> 


 <action path="/server" 


 type="com.changcheng.web.struts.actions.AndroidWebServer" name="dataForm" 


 scope="request" parameter="method"> 


 <forward name="success" path="/WEB-INF/pages/success.jsp"/> 


 </action> 


 </action-mappings> 


</struts-config> 






二、创建Android应用 


 1.创建Android工程 


 Project name:AndroidWebClient 


 BuildTarget:Android2.1 


 Application name:AndroidWEB应用客户端 


 Package name:com.changcheng.web.client 


 Create Activity:AndroidWebClient 


 Min SDK Version:7 




 2.AndroidManifest.xml 


<?xml version="1.0" encoding="utf-8"?> 


<manifest xmlns:android="http://schemas.android.com/apk/res/android" 


 package="com.changcheng.web.client" android:versionCode="1" 


 android:versionName="1.0"> 


 <application android:icon="@drawable/icon" android:label="@string/app_name"> 


 <!-- 单元测试 --> 


 <uses-library android:name="android.test.runner" /> 


 <activity android:name=".AndroidWebClient" android:label="@string/app_name"> 


 <intent-filter> 


 <action android:name="android.intent.action.MAIN" /> 


 <category android:name="android.intent.category.LAUNCHER" /> 


 </intent-filter> 


 </activity> 




 </application> 


 <uses-sdk android:minSdkVersion="7" /> 


 <!-- 访问internet权限 --> 


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


 <!-- 在SDCard中创建与删除文件权限 --> 


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


 <!-- 往SDCard写入数据权限 --> 


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


 <!-- 单元测试 --> 


 <instrumentation android:name="android.test.InstrumentationTestRunner" 


 android:targetPackage="com.changcheng.web.client" android:label="Tests for My App" /> 


</manifest> 


 Android应用要访问Internet需要添加权限。 




 3.ClientService类 


package com.changcheng.web.client.service; 




import java.io.ByteArrayOutputStream; 


import java.io.DataOutputStream; 


import java.io.File; 


import java.io.FileInputStream; 


import java.io.InputStream; 


import java.net.HttpURLConnection; 


import java.net.URL; 


import java.util.HashMap; 


import java.util.Map; 


import android.os.Environment; 


import android.util.Log; 




public class ClientService { 




 private static final String TAG = "ClientService"; 




 // 以get方式发送请求 


 public static void sendDataToServerByGet() throws Exception { 


 // 主机地址不可以设置为localhost或127.0.0.1,必须是本机或其他机器所在Internet网或局域网地址。 


 String path = "http://192.168.0.2:8080/AndroidWebServer/server.do?" 


 + "method=sendDataByGet&name=changcheng"; 


 URL url = new URL(path); 


 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 


 conn.setConnectTimeout(6 * 1000); 


 // 请求成功 


 if (conn.getResponseCode() == 200) { 


 // 获取服务器返回的数据 


 byte[] data = readStream(conn.getInputStream()); 


 Log.i(TAG, new String(data, "UTF-8")); 


 } 


 } 




 // 以Post方式发送请求,面向HTTP协议编程 


 public static void sendDataTOserverByPost() throws Exception { 


 String path = "http://192.168.0.2:8080/AndroidWebServer/server.do"; 


 String params = "method=sendDataByPost&name=tingting";// 请求参数 


 byte[] data = params.getBytes(); 


 URL url = new URL(path); 


 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 


 conn.setConnectTimeout(6 * 1000); 


 conn.setDoOutput(true);// 发送POST请求必须设置允许输出 


 conn.setUseCaches(false);// 不使用Cache 


 conn.setRequestMethod("POST"); 


 conn.setRequestProperty("Connection", "Keep-Alive");// 维持长连接 


 conn.setRequestProperty("Charset", "UTF-8"); 


 conn.setRequestProperty("Content-Length", String.valueOf(data.length)); 


 conn.setRequestProperty("Content-Type", 


 "application/x-www-form-urlencoded"); 


 DataOutputStream outStream = new DataOutputStream(conn 


 .getOutputStream()); 


 outStream.write(data);// 以内容实体方式发送请求参数 


 outStream.flush(); 


 outStream.close(); 


 // 请求成功 


 if (conn.getResponseCode() == 200) { 


 // 获取服务器返回的数据 


 byte[] html = readStream(conn.getInputStream()); 


 Log.i(TAG, new String(html, "UTF-8")); 


 } 


 } 




 // 以表单方式发送请求 


 public static void sendDataToServerByForm() throws Exception { 


 Map<String, String> params = new HashMap<String, String>(); 


 params.put("method", "sendDataByForm"); 


 params.put("strData", "字符串数据"); 


 // 获取SDCard中的good.jpg 


 File file = new File(Environment.getExternalStorageDirectory(), 


 "app_Goog_Android_w.png"); 


 FormFile fileData = new FormFile("app_Goog_Android_w.png", new FileInputStream(file), 


 "fileData", "application/octet-stream"); 


 HttpRequester.post( 


 "http://192.168.0.2:8080/AndroidWebServer/server.do", params, 


 fileData); 


 } 




 // 获取输入流数据 


 private static byte[] readStream(InputStream inStream) throws Exception { 


 byte[] buffer = new byte[1024]; 


 int len = -1; 


 ByteArrayOutputStream outStream = new ByteArrayOutputStream(); 


 while ((len = inStream.read(buffer)) != -1) { 


 outStream.write(buffer, 0, len); 


 } 


 byte[] data = outStream.toByteArray(); 


 outStream.close(); 


 inStream.close(); 


 return data; 


 } 


} 


 其中使用到的FormFile类: 


package com.changcheng.web.client.service; 




import java.io.InputStream; 




/** 


 * 上传文件 


 */ 


public class FormFile { 


 /* 上传文件的数据 */ 


 private byte[] data; 


 private InputStream inStream; 


 /* 文件名称 */ 


 private String filname; 


 /* 表单字段名称*/ 


 private String formname; 


 /* 内容类型 */ 


 private String contentType = "application/octet-stream"; 




 public FormFile(String filname, byte[] data, String formname, String contentType) { 


 this.data = data; 


 this.filname = filname; 


 this.formname = formname; 


 if(contentType!=null) this.contentType = contentType; 


 } 




 public FormFile(String filname, InputStream inStream, String formname, String contentType) { 


 this.filname = filname; 


 this.formname = formname; 


 this.inStream = inStream; 


 if(contentType!=null) this.contentType = contentType; 


 } 




 public InputStream getInStream() { 


 return inStream; 


 } 




 public void setInStream(InputStream inStream) { 


 this.inStream = inStream; 


 } 




 public byte[] getData() { 


 return data; 


 } 




 public void setData(byte[] data) { 


 this.data = data; 


 } 




 public String getFilname() { 


 return filname; 


 } 




 public void setFilname(String filname) { 


 this.filname = filname; 


 } 




 public String getFormname() { 


 return formname; 


 } 




 public void setFormname(String formname) { 


 this.formname = formname; 


 } 




 public String getContentType() { 


 return contentType; 


 } 




 public void setContentType(String contentType) { 


 this.contentType = contentType; 


 } 




} 




 其中使用到的HttpRequester类: 


package com.changcheng.web.client.service; 




import java.io.DataOutputStream; 


import java.io.InputStream; 


import java.net.HttpURLConnection; 


import java.net.URL; 


import java.util.ArrayList; 


import java.util.List; 


import java.util.Map; 


import org.apache.http.HttpResponse; 


import org.apache.http.NameValuePair; 


import org.apache.http.client.entity.UrlEncodedFormEntity; 


import org.apache.http.client.methods.HttpPost; 


import org.apache.http.impl.client.DefaultHttpClient; 


import org.apache.http.message.BasicNameValuePair; 


import org.apache.http.protocol.HTTP; 


import org.apache.http.util.EntityUtils; 


import android.util.Log; 




/** 


 * http请求发送器 


 */ 


public class HttpRequester { 


 /** 


 * 直接通过HTTP协议提交数据到服务器,实现如下面表单提交功能: 


 * <FORM METHOD=POST ACTION="http://192.168.0.200:8080/ssi/fileload/test.do" enctype="multipart/form-data"> 


 <INPUT TYPE="text" NAME="name"> 


 <INPUT TYPE="text" NAME="id"> 


 <input type="file" name="imagefile"/> 


 <input type="file" name="zip"/> 


 </FORM> 


 * @param actionUrl 上传路径(注:避免使用localhost或127.0.0.1这样的路径测试,因为它会指向手机模拟器,你可以使用http://www.itcast.cn或http://192.168.1.10:8080这样的路径测试) 


 * @param params 请求参数 key为参数名,value为参数值 


 * @param file 上传文件 


 */ 


 public static String post(String actionUrl, Map<String, String> params, FormFile[] files) { 


 try { 


 String BOUNDARY = "---------7d 4a6d158c9"; //数据分隔线 


 String MULTIPART_FORM_DATA = "multipart/form-data"; 




 URL url = new URL(actionUrl); 


 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 


 conn.setConnectTimeout(6* 1000); 


 conn.setDoInput(true);//允许输入 


 conn.setDoOutput(true);//允许输出 


 conn.setUseCaches(false);//不使用Cache 


 conn.setRequestMethod("POST"); 


 conn.setRequestProperty("Connection", "Keep-Alive"); 


 conn.setRequestProperty("Charset", "UTF-8"); 


 conn.setRequestProperty("Content-Type", MULTIPART_FORM_DATA + "; boundary=" + BOUNDARY); 




 StringBuilder sb = new StringBuilder(); 


 for (Map.Entry<String, String> entry : params.entrySet()) {//构建表单字段内容 


 sb.append("--"); 


 sb.append(BOUNDARY); 


 sb.append("\r\n"); 


 sb.append("Content-Disposition: form-data; name=\""+ entry.getKey() + "\"\r\n\r\n"); 


 sb.append(entry.getValue()); 


 sb.append("\r\n"); 


 } 


 DataOutputStream outStream = new DataOutputStream(conn.getOutputStream()); 


 outStream.write(sb.toString().getBytes());//发送表单字段数据 


 for(FormFile file : files){//发送文件数据 


 StringBuilder split = new StringBuilder(); 


 split.append("--"); 


 split.append(BOUNDARY); 


 split.append("\r\n"); 


 split.append("Content-Disposition: form-data;name=\""+ file.getFormname()+"\";filename=\""+ file.getFilname() + "\"\r\n"); 


 split.append("Content-Type: "+ file.getContentType()+"\r\n\r\n"); 


 outStream.write(split.toString().getBytes()); 


 if(file.getInStream()!=null){ 


 byte[] buffer = new byte[1024]; 


 int len = 0; 


 while((len = file.getInStream().read(buffer))!=-1){ 


 outStream.write(buffer, 0, len); 


 } 


 file.getInStream().close(); 


 }else{ 


 outStream.write(file.getData(), 0, file.getData().length); 


 } 


 outStream.write("\r\n".getBytes()); 


 } 


 byte[] end_data = ("--" + BOUNDARY + "--\r\n").getBytes();//数据结束标志 


 outStream.write(end_data); 


 outStream.flush(); 


 int cah = conn.getResponseCode(); 


 if (cah != 200) throw new RuntimeException("请求url失败"); 


 InputStream is = conn.getInputStream(); 


 int ch; 


 StringBuilder b = new StringBuilder(); 


 while( (ch = is.read()) != -1 ){ 


 b.append((char)ch); 


 } 


 Log.i("ItcastHttpPost", b.toString()); 


 outStream.close(); 


 conn.disconnect(); 


 return b.toString(); 


 } catch (Exception e) { 


 throw new RuntimeException(e); 


 } 


 } 




 /** 


 * 提交数据到服务器 


 * @param actionUrl 上传路径(注:避免使用localhost或127.0.0.1这样的路径测试,因为它会指向手机模拟器,你可以使用http://www.itcast.cn或http://192.168.1.10:8080这样的路径测试) 


 * @param params 请求参数 key为参数名,value为参数值 


 * @param file 上传文件 


 */ 


 public static String post(String actionUrl, Map<String, String> params, FormFile file) { 


 return post(actionUrl, params, new FormFile[]{file}); 


 } 




 /** 


 * 提交数据到服务器 


 * @param actionUrl 上传路径(注:避免使用localhost或127.0.0.1这样的路径测试,因为它会指向手机模拟器,你可以使用http://www.itcast.cn或http://192.168.1.10:8080这样的路径测试) 


 * @param params 请求参数 key为参数名,value为参数值 


 */ 


 public static String post(String actionUrl, Map<String, String> params) { 


 HttpPost httpPost = new HttpPost(actionUrl); 


 List<NameValuePair> list = new ArrayList<NameValuePair>(); 


 for (Map.Entry<String, String> entry : params.entrySet()) {//构建表单字段内容 


 list.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); 


 } 


 try { 


 httpPost.setEntity(new UrlEncodedFormEntity(list, HTTP.UTF_8)); 


 HttpResponse httpResponse = new DefaultHttpClient().execute(httpPost); 


 if(httpResponse.getStatusLine().getStatusCode() == 200){ 


 return EntityUtils.toString(httpResponse.getEntity()); 


 } 


 } catch (Exception e) { 


 throw new RuntimeException(e); 


 } 


 return null; 


 } 


} 


 我们最好对HTTP协议有深入的了解,这样在编写简单数据交互应用时直接面向HTTP协议编程可以提高运行速度并减少资源的占用。 


 我们在最后一个方法中使用到的HttpPost类,是Apache开源组织提供的httpcomponents-client-4.0.1包。httpcomponents-client-4.0.1可以实现浏览器的大部分功能,但如果我们能不使用它就尽量不使用它,因为这会造成对手机硬件资源的占用,从而减慢应用程序的运行速度。 




 4.测试类 


package com.changcheng.web.client.test; 




import com.changcheng.web.client.service.ClientService; 


import android.test.AndroidTestCase; 




public class TestAndroidClientService extends AndroidTestCase { 




 public void testSendDataToServerByGet() throws Throwable { 


 ClientService.sendDataToServerByGet(); 


 } 




 public void testSendDataToServerByPost() throws Throwable { 


 ClientService.sendDataTOserverByPost(); 


 } 




 public void testSendDataToServerByForm() throws Throwable { 


 ClientService.sendDataToServerByForm(); 


 } 


}






5.运行

首先启动AndroidWebService应用程序,然后运行测试方法,查看运行结果。