[j2me]利用kSOAP让MIDP设备与WebService之间传递类对象

news/2024/6/16 17:48:57

[j2me]利用kSOAPMIDP设备与

WebService之间传递类对象

编写者

日期

关键词

郑昀@ultrapower

2005-8-14

J2me webservice soa ksoap serialization MIDP CLDC

中国移动GPRS网络的连接方式有两种类型,一种是WAPGPRS,接入名称叫CMWAP,一种是KJavaGPRS,接入名称叫CMNET。也就是说,我们使用J2ME访问远端服务器的Web Service,将走移动的CMNET接入点。

MIDP的概念

MIDP的概念我就不再说了,有一个不错的图片,可以作为注脚:

kSOAPJSR172

原则上,我们可以通HttpConnection接口访问远端Web服务器

但是,对于远端的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

org.ksoap. ClassMap

org.ksoap. SoapObject

org.ksoap.transport. HttpTransport

 

ClassMap能够让你指定序列化/反序列化SOAP Messages的方式;

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/XMLWeb Services;二是使其具有解析XML据的能力。实现这两大功能,JSR172新定了提供相功能的两个包。这两个包占用存非常少,XML-RPC部分大需要25-30KB的空,而XML解析器需要35KB左右。

但是目前上市的手机很少有支持JSR172的,所以我们不得不使用kSOAP

      

Web ServiceString

Web Service传递StringMIDP是一件很简单的事情

在服务器端,你的由AXIS支撑的Web Service可以这么输出:

服务器端

public class SimpleKSoapWS {

   

    public SimpleKSoapWS () {

    }

   

    public String  foo(String username, String password) {

        return “fooResult”;

}

}

 

我们来说说,kSOAP是如何调用Web Service的:

SoapObject,一个高度抽象化的类,让无线设备完成SOAP调用。可以调用它的addProperty方法填写要调用的Web Service方法的参数。如下面代码所示:

SoapObject soap = new SoapObject(serviceNamespace, methodName);

SoapObject构造函数的两个参数的意思分别是:

serviceNamespace – 你的Web Service的命名空间;

methodName – 你要调用方法的名字。

然后,按照Web Service方法参数的顺序,依次调用

soap.addProperty( "username", "user" );

soap.addProperty( "password", "pass" );

 

一般来说,对于仅仅是String的返回值,还用不着ClassMap

接下来就要声明

HttpTransport tx = new HttpTransport(serviceUrl, methodName);

了。这是一个强大的helper类,来完成Http-call transport process,它封装了network的一切,你完全不用考虑序列化消息。方法HttpTransport.call()自己就能够发送请求给服务器、接收服务器响应并序列化SOAP消息,如下所示:

Object Response = tx.call(request);

客户端的MIDlet的按键事件函数这么写,即可:

midlet

   /*

     * 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;

                                  

                     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);

                     classMap.prefixMap = new PrefixMap( classMap.prefixMap, "ns",

                                   serviceNamespace );

                                  

                 transport = new HttpTransport(serviceUrl, soapAction);

                 transport.setClassMap(classMap);

                                         

                SoapObject soap = new SoapObject(

serviceNamespace, methodName);

                soap.addProperty( "username", "user" );

             soap.addProperty( "password", "pass" );

                                

                SoapObject response = (SoapObject)transport.call(soap);

        

                String sResponse = (String)response;

                                  

                            }

                   catch (Exception e) {

                                  e.printStackTrace ();

                   }

                     }

            };

            fetchThread.start();

                      

            display.setCurrent(mainForm);             

        }

这里的知识点是,我们需要

Thread fetchThread=new Thread(){…};

来另起一个线程来完成对Web Service的调用。否则,你的模拟器是会一动不动的。

Web Service

下面我们讲述如何MIDP设备和Web Service之间传递较为复杂的类,比如这个类中不但有String类型成员变量,还有Vector之类的复杂类型

kSoapFAQ上看,他们推荐使用KvmSerializable以及 ClassMap传递自定义类,但是我一直没有试验成功。

我还是按照能试验出来的办法讲述一下步骤吧:

大致思路就是,在服务器端将类实例按照一定规格(一个一个的成员变量写)序列化为byte[],将这个byte[]数组返回给kSOAPkSOAP收到之后反序列化,将byte[]一段一段地读入类实例。

Web Service

先来定义要传递的wsTeam类:

类定义

public class wsTeam{

private String wsReturnCode;

private String wsPersonCount;

public StringVector  wsvPersonName;

public byte[] serialize();

public static wsTeam deserialize(byte[] data) ;

}

其中,StringVector类是另外一个自定义类,就是简单地把String[]封装了一下,便于操作。StringVector类定义在附录中可以找到。

服务器端主要是序列化,所以我们来讲讲wsTeambyte[] serialize()函数。

序列化

       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();

       }

这样,类实例就可以把自己序列化为byte[]数组。

那么,Web Service可以这么提供:

服务器端

public class SimpleKSoapWS {

   

    public SimpleKSoapWS () {

    }

   

    public byte[]  foo2(String username, String password) {

        wsTeam obj= new wsTeam ();

           return obj.serialize();

}

}

到了MIDP设备上,要能够从byte[]恢复出wsTeam类实例才行。

 

StringVector的序列化方法writeObject也很简单,先写入字符串数组的大小,然后再将每一个元素写入,如下所示:

StringVector的序列化

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

              {

                     s.writeUTF(data[i]);

              }

       }

}

 

MIDP

和前面的MIDlet代码差不多,只不过要我们的ClassMap出场了

使用ClassMap

ClassMap classMap = new ClassMap();

(new MarshalBase64()).register(classMap);

HttpTransport tx = new HttpTransport(serviceUrl, methodName);

 

tx.setClassMap( classMap );

tx.debug = true;

MarshalBase64类定义在附录中可以找到。

这样,后面才能将接收到的SoapObject强制转换为byte[]

转换

Object Response = tx.call(request);

System.out.println( tx.responseDump );

byte[] by = (byte[])Response;

然后,再调用

反序列化

wsTeam wc = wsTeam.deserialize(by);

这样,在无线设备上就得到了wsTeam类实例了。

 

wsTeamdeserialize是这么定义的:

反序列化

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的反序列化方法readObject也很简单,先读入字符串数组的大小,就自行新建一个同样大小的字符串数组,然后再将每一个元素写入这个数组,如下所示:

StringVector的反序列化

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

              {

                     data[i] = s.readUTF();

              }

       }…

}

 

通过上面的反序列化,我们就可以通过

for (int i=0; i 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

 

 

AStringVector

StringVector

/**

 * 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

              {

                     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

              {

                     data[i] = s.readUTF();

              }

       }

}

 

BMarshalBase64

MarshalBase64

/**

 * @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);

 

    }

}

 





http://www.niftyadmin.cn/n/3649565.html

相关文章

盖茨比乔布斯_盖茨比中的自定义字体

盖茨比乔布斯Choosing the right font can add great value to a site and enhance the user experience. The right font-loading strategy, however, can be somewhat of a challenge. That’s why Gatsby provides several developer-friendly solutions for all of our fon…

Android中GridView使用

GridView(网格视图)是按照行列的方式来显示内容的&#xff0c;一般用于显示图片&#xff0c;图片等内容&#xff0c;比如实现九宫格图&#xff0c;用GridView是首选&#xff0c;也是最简单的。主要用于设置Adapter。 GridView常用的XML属性&#xff1a; 属性名称 描述 an…

旧版Chrome浏览器的安装和使用——chrome63

虽然新版的Chrome浏览器很好用&#xff0c;但是界面风格我还是喜欢旧版的浏览器。我的电脑上有新版和旧版两个。不过正常情况下谷歌浏览器只能安装一个。我的做法是新版的浏览器正常安装&#xff0c;旧版的有点类似eclipse&#xff0c;解压就可以使用。不过使用前面需要解决一个…

javascript 引擎_V8引擎和JavaScript优化技巧

javascript 引擎V8 is Google’s engine for compiling our JavaScript. Firefox has it’s own engine called SpiderMonkey, it’s quite similar to V8 but there are differences. We will be discussing the V8 engine in this article. V8是Google用来编译JavaScript的引…

Widget的基本知识与历史渊源

一、Widget是什么&#xff1f; App Widget是应用程序窗口小部件&#xff08;Widget&#xff09;是微型的应用程序视图&#xff0c;它可以被嵌入到其它应用程序中&#xff08;比如桌面&#xff09;并接收周期性的更新。你可以通过一个App Widget Provider来发布一个Widget。…

华为云青年开发班:黑白棋游戏开发

放假回家第一天&#xff0c;继续肝&#xff01;写一篇博客证明我还在&#xff01; 实验成果 任何时候都不要放弃自己的梦想&#xff0c;也许下一刻&#xff0c;梦想就会成真了呢&#xff01;

[论道]Sales职业生涯规划 作者:李楠

Sales职业生涯规划 作者&#xff1a;SPN睿商在线-李楠- 2005-08-02 聪明的Sales了解自己的职业生涯发展方向&#xff0c;知道自己适合做哪种类型的销售……讲述者和推销者 讲述型的Sales只会倾倒&#xff0c;推销型的Sales会洞察客户需求…… 孙&#xff1a;最近看了一本《竞争…

用switch语句编写程序_如何在Go中编写switch语句

用switch语句编写程序介绍 (Introduction) Conditional statements give programmers the ability to direct their programs to take some action if a condition is true and another action if the condition is false. Frequently, we want to compare some variable again…