对于byte字节数组来说,Axis2会自动将其序列化,然后使用Base64编码格式进行传输,因此,在服务端可以直接使用byte[]作为参数类型或返回值的类型。在下面的内容我们将会看到如何使用byte[]来重新改写服务端的程序。
服务端直接使用byte[]
在上一篇文章中,服务端使用了String类型的参数和返回值。这虽然也可以完成我们的任务,但Axis2会自动处理byte[]类型,因此,我们可以直接将String变成byte[]。例如,上传和下载图像的代码如下:
- // 上传图像
- public boolean updateByteImage(byte[] buffer)
- {
- try
- {
- FileOutputStream fos = new FileOutputStream("d:\\my.jpg");
- fos.write(buffer);
- fos.close();
- return true;
- }
- catch (Exception e)
- {
- // TODO: handle exception
- }
- return false;
- }
- // 下载图像
- public byte[] downloadByteImage()
- {
- byte[] buffer = new byte[8192];
- try
- {
- FileInputStream fis = new FileInputStream("e:\\my.jpg");
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- int count = 0;
- while ((count = fis.read(buffer)) >= 0)
- {
- baos.write(buffer, 0, count);
- }
- buffer = baos.toByteArray();
- fis.close();
- }
- catch (Exception e)
- {
- // TODO: handle exception
- }
- return buffer;
- }
在上面的代码中将参数和返回值的类型变成了byte[]。重新部署这个WebService,会得到如图1所示的WSDL。
图1 WSDL
在图1中的黑圈里的类型是xs:base64Binary,说明Axis2可以将byte[]转换成Base64格式的字符串。
对于客户端来说,代码并不需要做任何修改,完全感觉不到服务端使用的是String还是byte[]。将String类型改成byte[]类型后,效果和上一篇文章中的例子完全相同,下载图像的效果如图2所示。
图2 下载和显示图像
通过Base64编码传递对象(服务端实现)
在第一篇文章中直接将一个Product对象传到了客户端。除了这种方法外,还可以将对象序列化,并对其Base64编码后进行传递。例如,在本例中有一个Student类,代码如下:
- package service;
- import java.io.Serializable;
- public class Student implements Serializable
- {
- public String id;
- public String name;
- }
要想序列化对象,类必须实现java.io.Serializable接口。下面的代码是提供服务的SchoolService类。
- package service;
- import java.io.ByteArrayInputStream;
- import java.io.ObjectInputStream;
- public class SchoolService
- {
- public void setStudent(String base64)
- {
- try
- {
- byte[] buffer = new sun.misc.BASE64Decoder().decodeBuffer(base64);
- ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
- ObjectInputStream ois = new ObjectInputStream(bais);
- Student student = (Student) ois.readObject();
- System.out.println("id:" + student.id);
- System.out.println("name:" + student.name);
- }
- catch (Exception e)
- {
- }
- }
- }
在上面的代码中的base64参数表示base64编码格式 的序列化对象。在setStudent方法中通过解码将其还原成Student对象,然后输出id和name属性值。
通过Base64编码传递对象(客户端实现)
下面是客户端调用服务端的setStudent方法的代码。
- public void onClick_SetStudent(View view)
- {
- try
- {
- Student student = new Student();
- student.id = "4321";
- student.name = "超人";
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- ObjectOutputStream oos = new ObjectOutputStream(baos);
- oos.writeObject(student);
- byte[] buffer = baos.toByteArray();
- oos.close();
- baos.close();
- String base64 = new String(Base64.encodeBase64(buffer));
- String serviceUrl = "http://192.168.17.82:8080/axis2/services/SchoolService?wsdl";
- String methodName = "setStudent";
- SoapObject request = new SoapObject("http://service", methodName);
- request.addProperty("base64", base64);
- SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(
- SoapEnvelope.VER11);
- envelope.bodyOut = request;
- HttpTransportSE ht = new HttpTransportSE(serviceUrl);
- ht.call(null, envelope);
- envelope.getResponse();
- }
- catch (Exception e)
- {
- Log.d("setStudent_exception", String.valueOf(e));
- }
- }
在上面的代码中,首先创建了Student对象(要注意,这时在服务端和客户端都需要有一个Student类),并设置了Student对象的属性值。然后会使用下面的代码将Student对象编码成Base64格式。
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- ObjectOutputStream oos = new ObjectOutputStream(baos);
- oos.writeObject(student);
- byte[] buffer = baos.toByteArray();
- oos.close();
- baos.close();
- String base64 = new String(Base64.encodeBase64(buffer));
接下来的实现过程就是调用WebService,并传递Base64格式的参数。运行程序后,单击屏幕上方的按钮,就会在服务端的控制台输出id和name属性值,如图3所示。
图3 在服务端输出Student对象的属性值
脱离WebService,使用更自由的HTTP访问网络
在前面几篇文章中, 一直在使用WebService来与服务端进行交互,然而,使用WebService虽然可以很容易地传递数据,但也会受到很多限制,例如,由于WebService使用了XML格式传输数据,因此,在传递二进数据时必须将数据进行编码才能进行传递。这样将会使数据量明显增大,而且还会面临不同的WebService引擎不兼容的问题。因此,在接下来的内容会介绍一种更灵活的数据传输方式:HTTP。虽然WebService也是通过HTTP进行传输的,但对用户却是透明的。而通过HTTP可以直接来控制传输的过程。
HTTP是Internet中广泛使用的协议。几乎所有的语言和SDK都会不同程度地支持HTTP,当然, OPhone SDK也不例外。在OPhone SDK中可以采用多种方式使用HTTP,例如,HttpURLConnection、HttpGet、HttpPost等。
提交HTTP GET和HTTP POST请求
在这里首先要介绍一下OPhone SDK集成的Apache HttpClient模块。要注意的是,这里的Apache HttpClient模块是HttpClient 4.0(org.apache.http.*),而不是Jakarta Commons HttpClient 3.x(org.apache.commons.httpclient.*)。
在HttpClient模块中涉及到了两个重要的类:HttpGet和HttpPost。这两个类分别用来提交HTTP GET和HTTP POST请求。为了进行演示,需要先编写一个Servlet程序,用来接收Http Get和Http Post请求,代码如下:
- package net.blogjava.mobile;
- import java.io.IOException;
- import java.io.PrintWriter;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- /**
- * Servlet implementation class QueryServlet
- */
- public class QueryServlet extends HttpServlet
- {
- private static final long serialVersionUID = 1L;
- @Override
- protected void service(HttpServletRequest request,
- HttpServletResponse response) throws ServletException, IOException
- {
- response.setContentType("text/html;charset=utf-8");
- String queryStr = "";
- if ("post".equals(request.getMethod().toLowerCase()))
- queryStr = "POST请求;查询字符串:" + new String(request.getParameter("bookname").getBytes(
- "iso-8859-1"), "utf-8");
- else if ("get".equals(request.getMethod().toLowerCase()))
- queryStr = "GET请求;查询字符串:" + request.getParameter("bookname");
- String s =queryStr
- + "[Java Web开发速学宝典;Java开发指南思想(第4版);Java EE开发宝典;C#开发宝典]";
- PrintWriter out = response.getWriter();
- out.println(s);
- }
- }
在运行本例之前,需要先在PC上安装Tomcat,并将querybooks工程直接复制到<Tomcat安装目录>\webapps目录即可。然后启动Tomcat。在浏览器地址栏中输入如下的URL:
http://localhost:8080/querybooks/query.jsp
如果出现如图4所示的页面,说明querybooks已经安装成功。
图4 querybooks的测试页面
在querybooks工程中有一个QueryServlet类,访问这个类的URL如下:
http://192.168.17.82:8080/querybooks/QueryServlet?bookname=开发
其中“192.168.17.82”是PC的IP地址,bookname是QueryServlet的请求参数,表示图书名,通过该参数来查询图书信息。在图4所示的页面中的文本框内输入“开发”,然后单击【查询】按钮,页面会以HTTP POST方式向QueryServlet提交请求信息,如果成功提交,将显示如图5所示的内容。
图5 返回的响应信息
现在我们要通过HttpGet和HttpPost向QueryServlet提交请求信息,并将返回结果显示在TextView组件中。无论是使用HttpGet,还是使用HttpPost,都必须通过如下3步来访问HTTP资源。
1. 创建HttpGet或HttpPost对象,将要请求的Url通过构造方法传入HttpGet或HttpPost对象。
2. 使用DefaultHttpClient类的execute方法发送HTTP GET或HTTP POST请求,并返回HttpResponse对象。
3. 通过HttpResponse接口的getEntity方法返回响应信息,并进行相应的处理。
如果使用HttpPost方法提交HTTP POST请求,还需要使用HttpPost类的setEntity方法设置请求参数。
本例使用了两个按钮来分别提交HTTP GET和HTTP POST请求,并从EditText组件中获得请求参数(bookname)值,最后将返回结果显示在TextView组件中。两个按钮共用一个onClick事件方法,代码如下:
- public void onClick(View view)
- {
- // 读者需要将本例中的IP换成自己机器的IP
- String url = "http://192.168.17.82:8080/querybooks/QueryServlet";
- TextView tvQueryResult = (TextView) findViewById(R.id.tvQueryResult);
- EditText etBookName = (EditText) findViewById(R.id.etBookName);
- HttpResponse httpResponse = null;
- try
- {
- switch (view.getId())
- {
- // 提交HTTP GET请求
- case R.id.btnGetQuery:
- // 向url添加请求参数
- url += "?bookname=" + etBookName.getText().toString();
- // 第1步:创建HttpGet对象
- HttpGet httpGet = new HttpGet(url);
- // 第2步:使用execute方法发送HTTP GET请求,并返回HttpResponse对象
- httpResponse = new DefaultHttpClient().execute(httpGet);
- // 判断请求响应状态码,状态码为200表示服务端成功响应了客户端的请求
- if (httpResponse.getStatusLine().getStatusCode() == 200)
- {
- // 第3步:使用getEntity方法获得返回结果
- String result = EntityUtils.toString(httpResponse.getEntity());
- // 去掉返回结果中的“\r”字符,否则会在结果字符串后面显示一个小方格
- tvQueryResult.setText(result.replaceAll("\r", ""));
- }
- break;
- // 提交HTTP POST请求
- case R.id.btnPostQuery:
- // 第1步:创建HttpPost对象
- HttpPost httpPost = new HttpPost(url);
- // 设置HTTP POST请求参数必须用NameValuePair对象
- List<NameValuePair> params = new ArrayList<NameValuePair>();
- params.add(new BasicNameValuePair("bookname", etBookName
- .getText().toString()));
- // 设置HTTP POST请求参数
- httpPost.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
- // 第2步:使用execute方法发送HTTP POST请求,并返回HttpResponse对象
- httpResponse = new DefaultHttpClient().execute(httpPost);
- if (httpResponse.getStatusLine().getStatusCode() == 200)
- {
- // 第3步:使用getEntity方法获得返回结果
- String result = EntityUtils.toString(httpResponse.getEntity());
- // 去掉返回结果中的“\r”字符,否则会在结果字符串后面显示一个小方格
- tvQueryResult.setText(result.replaceAll("\r", ""));
- }
- break;
- }
- }
- catch (Exception e)
- {
- tvQueryResult.setText(e.getMessage());
- }
- }
运行本例,在文本编辑框中输入“开发”,并单击【GET查询】和【POST查询】按钮,会在屏幕下方法显示如图6和图7所示的信息。
图6 Get请求查询结果
图7 Post请求查询结果
总结
本文主要介绍了如何通过Base64编码来传递可序列化的对象。通过对二进制进行编码,我们可以传递任何形式的数据。虽然WebService可以满足大多数要求。但如果想更灵活地传输数据,直接使用HTTP就显得非常必要。在下一篇文章中将着重介绍HTTP传输数据的各种方法。
相关文章:
作者介绍
李宁,东北大学计算机专业硕士,拥有超过10年的软件开发经验。曾任国内某知名企业项目经理;目前担任eoeandroid和ophonesdn版主;中国移动开发者社区OPhone专家;51CTO客作专家;CSDN博客专家。曾领导并参与开发了多个大中型项目。目前主要从事Android及其相关产品的研发。从2005年进入写作领域以来,为《程序员》、《电脑编程技巧与维护》、《电脑报》、IT168、天极网等平面媒体和网络媒体撰写了一百多篇原创技术和评论文章。并在个人blog(http://nokiaguy.blogjava.net)上发表了大量的原创技术文章。2007年获《电脑编程技巧与维护》优秀作者。2009年获得OPhone征文大赛二等奖。个人著作:《Android/OPhone开发完全讲义》、《人人都玩开心网:Ext JS+Android+SSH整合开发Web与移动SNS》、《Java Web开发速学宝典》。
(声明:本网的新闻及文章版权均属OPhone SDN网站所有,如需转载请与我们编辑团队联系。任何媒体、网站或个人未经本网书面协议授权,不得进行任何形式的转载。已经取得本网协议授权的媒体、网站,在转载使用时请注明稿件来源。)