[j2me]利用kSOAP让MIDP设备与WebService之间传递类对象
2007-12-17 10:47
323 查看
[j2me]利用kSOAP让MIDP设备与
WebService之间传递类对象
编写者 | 日期 | 关键词 |
郑昀@ultrapower | 2005-8-14 | J2me webservice soa ksoap serialization MIDP CLDC |
无线设备联网的概念
中国移动GPRS网络的连接方式有两种类型,一种是WAP+GPRS,接入名称叫CMWAP,一种是KJava+GPRS,接入名称叫CMNET。也就是说,我们使用J2ME访问远端服务器的Web Service,将走移动的CMNET接入点。MIDP的概念
MIDP的概念我就不再说了,有一个不错的图片,可以作为注脚:kSOAP和JSR172
原则上,我们可以通过HttpConnection接口访问远端Web服务器。[/i][/i]
但是,对于远端的Web Service,我们还有两种解决方案:
l kSOAP;
l JSR172。
下面,我们来介绍一下:
kSOAP
http://ksoap.objectweb.org/上,你可以下载一系列的包:ksoap-j2se.zip
ksoap-midp.zip
ksoap-servlet.zip。
这是一个开源项目,它帮助你完成了SOAP的解析和调用工作。虽然kSOAP小组现在已经停止了开发,但是他们留下来的是一个能够工作的kSOAP。
让我们先熟悉一下即将用到的kSOAP的接口:
ListAdapter.cs[/b] [/b] |
org.ksoap. ClassMap org.ksoap. SoapObject org.ksoap.transport. HttpTransport |
SoapObject让你自如地构造SOAP调用;
HttpTransport为你屏蔽了Internet访问/请求和获取服务器SOAP的细节。
JSR172
在http://jcp.org/en/jsr/detail?id=172,您可以找到“J2METM Web Services Specification”的04年最终发布的规范http://jcp.org/aboutJava/communityprocess/final/jsr172/index.html。
还有http://java.sun.com/products/wsa/,这里的WSA指的是J2ME Web Services APIs。
“如何真正地将移动设备融入到Web Services中去呢?这就需要使得PDA、手机等成为Web Services的客户端,那么这些设备至少应该具有处理XML信息的能力。J2ME Web Services规范(JSR172)正是为了解决这个问题而来。
首先,该规范的制订是为了给J2ME平台增加两大功能:一是使其能够远程访问基于SOAP/XML的Web Services;二是使其具有解析XML数据的能力。为了实现这两大功能,JSR172新定义了提供相应功能的两个可选包。这两个包占用内存非常少,XML-RPC部分大概需要25-30KB的空间,而XML解析器则需要35KB左右。”
但是目前上市的手机很少有支持JSR172的,所以我们不得不使用kSOAP。
第一小步,Web Service传递String
Web Service传递String给MIDP是一件很简单的事情:在服务器端,你的由AXIS支撑的Web Service可以这么输出:
服务器端[/b] [/b] |
public class SimpleKSoapWS { public SimpleKSoapWS () { } public String foo(String username, String password) { return “fooResult”; } } |
SoapObject,一个高度抽象化的类,让无线设备完成SOAP调用。可以调用它的addProperty方法填写要调用的Web Service方法的参数。如下面代码所示:
SoapObject soap = new SoapObject(serviceNamespace, methodName);
[/b]
SoapObject构造函数的两个参数的意思分别是:
serviceNamespace – 你的Web Service的命名空间;
methodName – 你要调用方法的名字。
然后,按照Web Service方法参数的顺序,依次调用
soap.addProperty( "username", "user" );
[/b]
soap.addProperty( "password", "pass" );
[/b]
一般来说,对于仅仅是String的返回值,还用不着ClassMap。
接下来就要声明
HttpTransport tx = new HttpTransport(serviceUrl, methodName);
[/b]
了。这是一个强大的helper类,来完成Http-call transport process,它封装了network的一切,你完全不用考虑序列化消息。方法HttpTransport.call()自己就能够发送请求给服务器、接收服务器响应并序列化SOAP消息,如下所示:
Object Response = tx.call(request);
[/b]
客户端的MIDlet的按键事件函数这么写,即可:
midlet[/b] [/b] |
/* * Respond to commands, including exit * On the exit command, cleanup and notify that the MIDlet has been destroyed. */ public void commandAction(Command c, Displayable s) { if (c == exitCommand) { destroyApp(false); notifyDestroyed(); } if (c == connectCommand) { //匿名内部Thread,访问远程服务。 Thread fetchThread=new Thread() { public void run(){ try { HttpTransport transport = null; [/b] String serviceNamespace = "http://localhost:8080/ SimpleWS /services/ SimpleKSoapWS "; String methodName = "foo"; String serviceUrl = "http://localhost:8080/ SimpleWS /services/ SimpleKSoapWS?wsdl"; ClassMap classMap = new ClassMap(true); [/b] classMap.prefixMap = new PrefixMap( classMap.prefixMap, "ns", [/b] serviceNamespace ); [/b] transport = new HttpTransport(serviceUrl, soapAction); [/b] transport.setClassMap(classMap); [/b] SoapObject soap = new SoapObject( [/b] serviceNamespace, methodName); [/b] soap.addProperty( "username", "user" ); [/b] soap.addProperty( "password", "pass" ); [/b] SoapObject response = (SoapObject)transport.call(soap); [/b] String sResponse = (String)response; [/b] } catch (Exception e) { e.printStackTrace (); } } }; fetchThread.start(); display.setCurrent(mainForm); } |
Thread fetchThread=new Thread(){…};
来另起一个线程来完成对Web Service的调用。否则,你的模拟器是会一动不动的。
第二小步,Web Service传递较为复杂的类
下面我们讲述如何在MIDP设备和Web Service之间传递较为复杂的类,比如这个类中不但有String类型成员变量,还有Vector之类的复杂类型。从kSoap的FAQ上看,他们推荐使用KvmSerializable以及 ClassMap传递自定义类,但是我一直没有试验成功。
我还是按照能试验出来的办法讲述一下步骤吧:
大致思路就是,在服务器端将类实例按照一定规格(一个一个的成员变量写)序列化为byte[],将这个byte[]数组返回给kSOAP。kSOAP收到之后,再反序列化,将byte[]一段一段地读入类实例。
Web Service服务器端的做法
先来定义要传递的wsTeam类:类定义[/b] [/b] |
public class wsTeam{ private String wsReturnCode; private String wsPersonCount; public StringVector wsvPersonName; public byte[] serialize(); public static wsTeam deserialize(byte[] data) ; } |
服务器端主要是序列化,所以我们来讲讲wsTeam的byte[] serialize()函数。
序列化[/b] [/b] |
public byte[] serialize() { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); try { dos.writeUTF(wsReturnCode); dos.writeUTF(wsPersonCount); wsvPersonName.writeObject(dos); baos.close(); dos.close(); } catch(Exception exc) { exc.printStackTrace(); } return baos.toByteArray(); } |
那么,Web Service可以这么提供:
服务器端[/b] [/b] |
public class SimpleKSoapWS { public SimpleKSoapWS () { } public byte[] foo2(String username, String password) { wsTeam obj= new wsTeam (); return obj.serialize(); } } |
StringVector的序列化方法writeObject也很简单,先写入字符串数组的大小,然后再将每一个元素写入,如下所示:
StringVector[/b]的序列化[/b] [/b] |
public class StringVector {… public synchronized void writeObject(java.io.DataOutputStream s) throws java.io.IOException { // Write out array length s.writeInt(count); // Write out all elements in the proper order. for (int i=0; i<count; i++) { s.writeUTF(data[i]); } } … } |
MIDP设备的做法
和前面的MIDlet代码差不多,只不过要我们的ClassMap出场了:使用[/b]ClassMap[/b] [/b] |
ClassMap classMap = new ClassMap(); [/b] (new MarshalBase64()).register(classMap); [/b] HttpTransport tx = new HttpTransport(serviceUrl, methodName); tx.setClassMap( classMap ); [/b] tx.debug = true; |
这样,后面才能将接收到的SoapObject强制转换为byte[]。
转换[/b] [/b] |
Object Response = tx.call(request); System.out.println( tx.responseDump ); byte[] by = (byte[])Response; [/b] |
反序列化[/b] [/b] |
wsTeam wc = wsTeam.deserialize(by); |
wsTeam的deserialize是这么定义的:
反序列化[/b] [/b] |
public class StringVector {… public static wsTeam deserialize(byte[] data) { ByteArrayInputStream bais = new ByteArrayInputStream(data); DataInputStream dis = new DataInputStream(bais); wsTeam wc = new wsTeam(); try { wc.wsReturnCode = dis.readUTF(); wc.wsPersonCount = dis.readUTF(); wc. wsvPersonName.readObject(dis); bais.close(); dis.close(); } catch(Exception exc) exc.printStackTrace(); } return wc; } …} |
StringVector[/b]的反序列化[/b] [/b] |
public class StringVector {… public synchronized void readObject(java.io.DataInputStream s) throws java.io.IOException, ClassNotFoundException { // Read in array length and allocate array int arrayLength = s.readInt(); data = new String[arrayLength]; // 同步data的大小 count = arrayLength; // Read in all elements in the proper order. for (int i=0; i<arrayLength; i++) { data[i] = s.readUTF(); } }… } |
for (int i=0; i<wc. wsvPersonName.size(); i++)
{
System.out.println("第" + i +"个人:" +
wc. wsvPersonName.getStringAt(i));
}
来打印MIDlet上收到的类对象中的StringVector成员变量了。
小结
利用相同的办法,您还可以在无线设备和Web Service之间,传递各种各样的类对象,类里面也包含了各种类型的成员变量。编写者 | 日期 | 关键词 |
郑昀@ultrapower | 2005-8-19 | J2me webservice soa ksoap serialization MIDP CLDC |
[/b]
StringVector[/b] [/b] |
/** * Vector主要用来保存各种类型的对象(包括相同类型和不同类型的对象)。 * 但是在一些情况下使用会给程序带来性能上的影响。这主要是由Vector类的两个特点所决定的。 * 第一,Vector提供了线程的安全保护功能。即使Vector类中的许多方法同步。 * 但是如果你已经确认你的应用程序是单线程,这些方法的同步就完全不必要了。 * 第二,在Vector查找存储的各种对象时,常常要花很多的时间进行类型的匹配。 * 而当这些对象都是同一类型时,这些匹配就完全不必要了。 * 因此,有必要设计一个单线程的,保存特定类型对象的类或集合来替代Vector类 */ package com.ultrapower.helper; /** * @author VictorZheng * */ public class StringVector { // 这儿的transient标示这个属性不需要自动序列化 private transient String[] data; private int count; public int size() { return count; } public StringVector() { // default size is 10 this(10); } public StringVector(int initialSize) { data = new String[initialSize]; } public void add(String str) { // ignore null strings if(str == null) { return; } ensureCapacity(count + 1); data[count++] = str; } private void ensureCapacity(int minCapacity) { int oldCapacity = data.length; if (minCapacity > oldCapacity) { String oldData[] = data; int newCapacity = oldCapacity * 2; data = new String[newCapacity]; System.arraycopy(oldData, 0, data, 0, count); } } public void remove(String str) { if(str == null) { return; // ignore null str } for(int i = 0; i < count; i++) { // check for a match if(data[i].equals(str)) { System.arraycopy(data,i+1,data,i,count-1); // copy data // allow previously valid array element be gc'd data[--count] = null; return; } } } public final String getStringAt(int index) { if(index < 0) { return null; } else if(index > count) { return null; // index is > # strings } else { return data[index]; // index is good } } public synchronized void writeObject(java.io.DataOutputStream s) throws java.io.IOException { // Write out array length s.writeInt(count); // Write out all elements in the proper order. for (int i=0; i<count; i++) { s.writeUTF(data[i]); } } public synchronized void readObject(java.io.DataInputStream s) throws java.io.IOException, ClassNotFoundException { // Read in array length and allocate array int arrayLength = s.readInt(); data = new String[arrayLength]; // 同步data的大小 count = arrayLength; // Read in all elements in the proper order. for (int i=0; i<arrayLength; i++) { data[i] = s.readUTF(); } } } |
[/b]
MarshalBase64 [/b] |
/** * @author VictorZheng * */ import java.io.IOException; import org.kobjects.serialization.ElementType; import org.ksoap.ClassMap; import org.ksoap.Marshal; import org.ksoap.Soap; import org.ksoap.SoapParser; import org.ksoap.SoapWriter; /** Base64 (de)serializer */ public class MarshalBase64 implements Marshal { static byte [] BA_WORKAROUND = new byte [0]; public static Class BYTE_ARRAY_CLASS = BA_WORKAROUND.getClass (); public Object readInstance (SoapParser parser, String namespace, String name, ElementType expected) throws IOException { parser.parser.read (); // start tag Object result = Base64.decode (parser.parser.readText ()); parser.parser.read (); // end tag return result; } public void writeInstance (SoapWriter writer, Object obj) throws IOException { writer.writer.write (Base64.encode ((byte[]) obj)); } public void register (ClassMap cm) { cm.addMapping (cm.xsd, "base64Binary", MarshalBase64.BYTE_ARRAY_CLASS, this); cm.addMapping (Soap.ENC, "base64", MarshalBase64.BYTE_ARRAY_CLASS, this); } } |
相关文章推荐
- [j2me]利用kSOAP让MIDP设备与WebService之间传递类对象[上]
- [j2me]利用kSOAP让MIDP设备与WebService之间传递类对象
- [j2me]利用kSOAP让MIDP设备与WebService之间传递类对象[下]
- [j2me]利用kSOAP让MIDP设备与WebService之间传递类对象
- 利用kSOAP让MIDP设备与WebService之间传递类对象
- 利用AXIS开发Webservice(三) —— 如何传递JavaBean和你的对象
- android通过ksoap访问webservice方法传递一个复杂对象参数
- ksoap调用cxf webservice传递类对象参数,返回类对象
- 线程之间传递数据对象(利用线程槽实现)
- Parcelable和Serializable(二)--利用Parcelable在Activity之间传递自定义对象
- 利用Bundle在activity之间传递对象
- 利用AXIS开发Webservice(三) —— 如何传递JavaBean和你的对象
- 利用Bundle在activity之间传递对象
- Parcelable和Serializable(二)--利用Parcelable在Activity之间传递自定义对象
- Android学习札记11:利用Parcelable在Activity之间传递对象参数的官方解释和示例
- Parcelable和Serializable(一)--利用Serializable在Activity之间传递自定义对象
- 利用Bundle在activity之间传递对象
- Parcelable和Serializable(一)--利用Serializable在Activity之间传递自定义对象
- Android中,利用Intent传递对象值
- 使用HttpURLConnection实现在android客户端和服务器之间传递对象