对于byte字节数组来说,Axis2会自动将其序列化,然后使用Base64编码格式进行传输,因此,在服务端可以直接使用byte[]作为参数类型或返回值的类型。在下面的内容我们将会看到如何使用byte[]来重新改写服务端的程序。
服务端直接使用byte[]
在上一篇文章中,服务端使用了String类型的参数和返回值。这虽然也可以完成我们的任务,但Axis2会自动处理byte[]类型,因此,我们可以直接将String变成byte[]。例如,上传和下载图像的代码如下:
?
view plain
copy to clipboard
print
?
- ??
- public?boolean?updateByteImage(byte[]?buffer)??
- {??
- ????try??
- ????{??????????????
- ????????FileOutputStream?fos?=?new?FileOutputStream("d:my.jpg");??
- ????????fos.write(buffer);??
- ????????fos.close();??
- ????????return?true;??
- ????}??
- ????catch?(Exception?e)??
- ????{??
- ??????????
- false;??
- }??
- ??????
- 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();??
- 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;??
class?Student?implements?Serializable??
{??
public?String?id;??
public?String?name;??
}??
要想序列化对象,类必须实现java.io.Serializable接口。下面的代码是提供服务的SchoolService类。
?
import
?java.io.ByteArrayInputStream;??
import?java.io.ObjectInputStream;??
??
class?SchoolService??
void?setStudent(String?base64)??
????????{??
????????????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);??
????????}??
????}??
在上面的代码中的base64参数表示base64编码格式 的序列化对象。在setStudent方法中通过解码将其还原成Student对象,然后输出id和name属性值。
通过Base64编码传递对象(客户端实现)
?
下面是客户端调用服务端的setStudent方法的代码。
void?onClick_SetStudent(View?view)??
????????????Student?student?=?new?Student();??
????????????student.id?=?"4321";??
????????????student.name?=?"超人";??
????????????ByteArrayOutputStream?baos?=? ????????????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?
?*/??
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事件方法,代码如下:
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;??
????{??
switch?(view.getId())??
??????????????
case?R.id.btnGetQuery:??
??????????????????
????????????????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)??
????????????????{??
??????????????????????
????????????????????String?result?=?EntityUtils.toString(httpResponse.getEntity());??
//??去掉返回结果中的“r”字符,否则会在结果字符串后面显示一个小方格??
????????????????????tvQueryResult.setText(result.replaceAll("r",?""));??
????????????????}??
????????????????break;??
??????????????
????????????case?R.id.btnPostQuery:??
??????????????????
????????????????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);??
????????tvQueryResult.setText(e.getMessage());??
????}??
运行本例,在文本编辑框中输入“开发”,并单击【GET查询】和【POST查询】按钮,会在屏幕下方法显示如图6和图7所示的信息。

图6? Get请求查询结果

图7? Post请求查询结果
总结
本文主要介绍了如何通过Base64编码来传递可序列化的对象。通过对二进制进行编码,我们可以传递任何形式的数据。虽然WebService可以满足大多数要求。但如果想更灵活地传输数据,直接使用HTTP就显得非常必要。在下一篇文章中将着重介绍HTTP传输数据的各种方法。
相关文章:
客户端和服务端通讯的N种方式(一)
客户端和服务端通讯的N种方式(二)
作者介绍
李宁,东北大学计算机专业硕士,拥有超过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网站所有,如需转载请与我们编辑团队联系。任何媒体、网站或个人未经本网书面协议授权,不得进行任何形式的转载。已经取得本网协议授权的媒体、网站,在转载使用时请注明稿件来源。)
(编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!