从何时选择移动 Web 服务到总体设计指导原则再到用于移动 Web 服务的值类型本文提出了在设计用于移动设备的 Web 服务时需要考虑的许多设计事项文中还介绍了许多设计移动 Web 服务方面的最佳实践从本文中您可以了解如何决定何时使用 Web 服务在设计 Web 服务时需要考虑什么事项以及在规划移动 Web 服务时必须谨记哪些问题
Web 服务是一种集成技术在集成异构系统时 Web 服务的价值可以得到最好的证明因为其支持许多类型的编程语言运行时环境和网络当需要从不兼容的环境连接应用程序时Web 服务就有了用武之地通过 Web 服务您可以将业务应用程序从 Java; Platform Enterprise Edition (JEE) 连接到 NET您还可以使用某个运行在 Linux; 中的应用程序将一个应用程序集成在 Windows; 操作环境中在本文中我提供了一些针对移动 Web 服务的重要设计考虑事项并且向您介绍了一些与之有关的最佳实践
首先我将讨论在开始之前 需要考虑哪些事项
开始之前
在开始设计整个系统的体系结构之前您必须做出如下决定——何时使用移动 Web 服务以及何时不使用移动 Web 服务
对于移动设备Web 服务是利用工作站的强大计算功能的一种最佳方式Java Specification Request (JSR) 定义了用于 Java Platform Micro Edition (JME) 平台的 Web 服务 API由于移动服务主要从客户端的角度进行编程并且是服务使用者因此本文只需要介绍一部分远程服务调用 API (JAXRPC) 和 JAXP (Java API for XML Parsing)
设计移动 Web 服务的主要目的在于使嵌入式设备能够使用由服务器提供的服务换句话说移动 Web 服务是从 Web 服务使用者的角度进行设计的目的在于支持轻量级设备共享服务器的计算功能和数据库
移动 Web 服务无缝地集成了运行在不同平台上的两种不同的应用程序并且提供了它们之间的互操作性通常在考虑移动设备的参与时有三种类型的集成技术可以运用
套接字通信
Web 服务
消息传递技术(如 WebSphere® MQ Everyplace)
与套接字通信和消息传递技术相比Web 服务有一些突出的优势Web 服务使用可扩展标记语言 (XML) 来传输消息(包括结构良好的数据信息)使用简单对象访问协议 (SOAP) 来传输对象如果您使用的是套接字通信则必须完全负责定义要传输的数据结构而且如果客户端和服务器是用不同的编程语言编写的(例如 C++ 和 Java 编程语言)则您的工作量将大大增加——您必须负责数据传输和 C++ 和 Java 编程中的编码细节
消息传递软件可能是一种解决方案但如果您所关注的是性能并且不担心事务和安全级别则消息传递软件真的不是一个非常好的选择如果使用消息传递软件您将花大量的时间和精力解决安全问题并且您的客户很有可能站在您的门口问为什么这么慢?
我不准备告诉您正确的选择应该是什么而给出一些理由来说明为什么 Web 服务可能是一个好的选择
其中的一个理由可能是服务器端编程即使是像 Web 服务这样好的机制也仍然由于 XML 处理以及传输和接收 SOAP 消息的开销的原因而不能满足严格的实时处理需求因此在设计时需要考虑两个问题
与普通的 HTTP 访问和专用的消息传递方法相比每次 Web 服务调用的开销都比较大所以当您主要考虑性能时您可能需要首先选择另一项技术
由于开销的原因如果您只需要在应用程序的各层之间进行通信就不必选择 Web 服务例如不要将 Web 服务放在应用程序的视图层和控制器层之间
现在我假定您已经决定使用 Web 服务那么在总体设计方面需要考虑哪些问题?
在决定使用 Web 服务之后
您需要考虑的下一个问题是 Web 服务的总体设计可以运用下列通用设计指导原则
管理 Web 服务的粒度
首先定义 Web 服务接口然后加以实现
使用 Document/literal 作为编码样式
优先选择 JavaBean 组件而不是 Enterprise JavaBean (EJB) 组件作为服务提供者
避免 XML 元素嵌套太深因为这可能大大延长解析封送处理和取消封送处理的时间
下面详细介绍这些设计考虑事项
管理 Web 服务粒度
关于粒度管理需要谨记以下几点
始终优先考虑粗粒度的 Web 服务决不要在分布式系统之间使用细粒度 Web 服务调用
Web 服务是一个很好的工具集但是当您以细粒度的方式使用 Web 服务时它可能对应用程序的性能有很大的影响因为 XML 解析序列化和反序列化的开销很高这种开销可能占处理时间的百分之三十
在本文中我以 Task Management 系统中的登录功能为例驱动程序首先登录到系统如果登录成功则有两列任务显示在屏幕上
对于此登录问题有两种解决方案
一种解决方案是首先调用登录方法然后当该方法调用返回 true 时调用一个方法来获取相应的任务
其他的事情将在一步中完成如果驱动程序登录到 Task Management 系统则返回驱动程序的角色以及分配的任务和启动的任务如果登录失败则返回指示驱动程序未登录到该系统的信息
对于第一种解决方案它是细粒度登录系统而显示任务列表需要两次调用 Web 服务这可能带来很大的延迟第二种解决方案是粗粒度登录系统它返回您在一次调用中需要的所有信息并确保由于网络延迟和系统 I/O 带来的影响最小
首先定义 Web 服务接口然后进行实现
这不仅从移动 Web 服务的角度来看是适用的而且从 Web 服务设计和(更高的层次)面向对象的软件设计来看也是适用的正如您所知道的接口是客户端和服务提供程序之间的契约而且保持稳定非常重要(因为正如您所知道的实现容易改变)
定义功能接口非常直接和直观——只需考虑
您的目标
您需要获取什么信息
您需要将何种结果返回到客户端
然后您应该将该接口的相关要点写下来并在这些要点的指导下完成所有实现细节
这是一条简单的设计指导原则了解您的目标非常重要目标可以驱动您编写测试用例并且指导您编写功能实现这也是为什么测试驱动开发 (TDD) 广泛地应用于各种开发技术的原因
使用 Document/literal 作为编码样式
目前JAXRPC 支持三种操作模式
RPC/encoded其优点在于简单接收方可以轻松地将消息发送到操作的实现其缺点在于类型编码信息的开销较大这会降低吞吐量性能
RPC/literal其优点与 RPC/encoded 相同而且 遵循 WSI 组织制定的规范
Document/literal其优点在于没有类型编码信息任何 XML 验证器都可以验证消息其缺点在于 SOAP 消息中缺少操作名所以发送消息很难甚至不可能
在一些资料中您还可能发现名为 Document/encoded(使用这种模式的人不多)和 Document/literal wrapped(由 Microsoft 定义但是没有相关规范其缺点在于比其他模式复杂)的模式对这些操作模式的详细解释可以在参考资料中的Which style of WSDL should I use?内找到
在这些模式中WSI 标准仅支持 RPC/literal 和 Document/literal对于移动 Web 服务JAXRPC 实现必须使用 Document/literal 将基于 Web 服务描述语言 (WSDL) 的服务描述映射到相应的 Java 表示形式因此如果您只使用 Document/literal 作为编码样式则您是最安全的
优先选择 JavaBeanser 组件而不是 EJB 组件作为服务提供程序
在使用 Java 编程语言公开服务时需要考虑两种类型的服务提供程序
从 JavaBean 组件生成 Web 服务
使用无状态会话 EJB 组件
虽然在某些情况下EJB 组件非常有用但是 JavaBeans 组件常常是更好的选择特别是在开发移动 Web 服务时实现使用 JavaBean 组件生成的服务提供程序比较简单和容易而且与相应的会话 EJB 组件相比JavaBean 组件更稳定但是如果您需要从使用 EJB 组件开发的现有 JEE 应用程序公开 Web 服务则请使用 EJB 组件
避免 XML 元素嵌套太深
如果数组的数组复杂类型的数组或包含另一个自定义复杂类型的复杂类型等嵌套太深则将大大影响 Web 服务的性能清单 显示一个 XML 描述示例——一个自定义数据类型 Task
类的数组
清单 自定义数据类型
import javaioSerializable;public class Task implements Serializable { /** * The id of the task */ private int taskID = ; /** * Owner name of the task */ private String ownerName; /** * public default nonargument constructor * */ public Task() { } /** * Constructor of the Task class * * @param taskID * id of the task * @param ownerName * owner name of the task */ public Task(int taskID String ownerName) { thistaskID = taskID; thisownerName = ownerName; } /** * @return Returns the ownerName */ public String getOwnerName() { return ownerName; } /** * @param ownerName * The ownerName to set */ public void setOwnerName(String ownerName) { thisownerName = ownerName; } /** * @return Returns the taskID */ public int getTaskID() { return taskID; } /** * @param taskID The taskID to set */ public void setTaskID(int taskID) { thistaskID = taskID; }}
如果一个方法返回如 清单 中定义的 Task 的数组则该方法的源代码包含在下面的清单中方法 getTasks() 返回一个由五个 Task 对象组成的数组如清单 所示
清单 返回自定义数据类型的数组的方法
public Task[] getTasks(String name){ Task[] tasks = new Task[]; for(int i=; i<; i++){ tasks[i] = new Task(i name); } return tasks; }
当使用 getTasks()(如清单 所示)所属的 JavaBean 组件公开 Web 服务时Task 类映射到其中包含 Task 类的名称空间的 tn:Task
清单 WSDL 定义中的 XML 数据类型
<complexType name=Task> <sequence <element name=ownerName nillable=true type=xsd:string/> <element name=taskID type=xsd:int/> </sequence></complexType>
同时数据类型 Task[]
映射到 ArrayOf_tn_Task
ArrayOf_tn_Task
的 XML 描述如清单 所示
清单 ArrayOf_tn_Task 的 XML 描述
<complexType name=ArrayOf_tns_Task> <sequence> <element maxOccurs=unbounded minOccurs= name=Task nillable=true type=tns:Task/> </sequence> </complexType>
如清单 所示为单个自定义复杂类型数组生成的 XML 描述很长相反Java 语言中的单个 String 类型映射到 xsd:string而没有生成 complexType 元素诸如 booleanint 和 byte 这样的基元类型分别映射到 xsd:booleanxsd:int 和 xsd:byte
您可能已经注意到 XML 元素的嵌套(避免嵌套太深)和粒度考虑(使用粗粒度)之间的沖突在实际运用中嵌套和粒度之间应该有一个平衡如果您更关注应用程序的性能则应该仔细地权衡这两个考虑事项以获得一个更好的解决方案
移动 Web 服务的设计考虑事项
我已经讨论了设计 Web 服务的指导原则现在我将把重点放在移动 Web 服务的考虑事项上在大多数情况下当将 JAXRPC 值类型用于移动 Web 服务时需要考虑一些事情JAXRPC 值类型(遵循 JSR)是 Java 类其值可以在服务客户端和服务端点之间移动为了获得一致的值类型必须遵循一系列规则我只列出其中的几条与本文关系最大的规则是
您必须具有公共缺省构造器
您必须具有用于需要在网络上传输的字段的 setter 和 getter 方法
在处理数据集合时您应该使用数组
移动 Web 服务中有一些首选的数据类型
在处理输入和输出参数时注意可能出现的问题
您必须具有公共缺省构造器
在反序列化的过程中SOAP 运行时环境使用缺省构造器来构造对象如果您试图在没有公共缺省构造器的情况下编写值类型(也称为数据传输对象)在当 JAXRPC 运行时尝试序列化和反序列化数据对象时可能会遇到错误对于像 IBM Rational® Application Developer (RAD) 这样的 IDE将不为该数据类型生成序列化和反序列化 Helper 类(由 RAD 通过前缀 _Helper
_Ser
和 _Deser
生成)所以在调用与自定义数据类型相关的方法时会出现序列化错误不带参数的构造器确保可以根据序列化状态远程构造对象
您必须具有用于网络传输字段的 setter 和 getter 方法
首先看一看清单 中的类 FailTask 的源代码
清单 FailTask 类的定义
public class FailTask { /** * The owner of the task */ private int ownerid; /** * The name of the task */ private String name; /** * Default public nonargument constructor * */ public FailTask(){ } /** * Constructor of FailTask class * @param ownerid Owner of the task * @param name Name of the task */ public FailTask(int ownerid String name){ thisownerid = ownerid; thisname = name; } /** * Getter method * @return the ownerid of the task */ public int getOwnerid(){ return ownerid; } /** * Setter method * @param ownerid the ownerid to be set */ public void setOwnerid(int ownerid){ thisownerid = ownerid; } }
您可以将清单 中所示的方法添加到 Web 服务中该方法将返回单个 FailTask 对象
清单 返回 FailTask 对象的方法public FailTask getFailTask(int ownerid String name){ return new FailTask(ownerid name); }
当使用 RAD 附带的 Universal Test Client 中的 和 Rachel 参数调用 getFailTask() 方法时所得到的响应如图 中所示
图 Universal Test Client 中的响应视图
name 字段在哪里?它不在这里因为我没有通过 getter 和 setter 方法提供 name 字段Setter 和 getter 方法是必须提供的两个方法和 FailTask_Ser
类中一样name 字段 getter 方法用于将 name 字段值写入 SOAP 消息在 FailTask_Deser
类中name 字段 setter 方法用于设置反序列化的 FailTask
对象的 name 值
在处理数据集合时您应该使用数组
为了有效地使用 Web 服务您必须或多或少地使用数据集合但是必须提醒当处理许多值类型时事情会变得比较麻烦因此需要考虑以下问题
当需要动态长度的数组时请考虑 ArrayList
您已经反复听说过如果不考虑同步则 ArrayList
比 Vector
更有效但遗憾的是JSR JAXRPC 规范没有强制要求支持 Java Collection 类型有些 Web 服务引擎可能没有为 ArrayList
提供支持例如IBM Web 服务引擎只正式支持 Java Collection Framework 中的一小部分类包括 javautilVector
javautilHashTable
和 javautilHashMap
那么尝试一下另一个动态数组 Vector
会如何呢?如果在相同平台上生成存根文件它将正常工作但是如果在不同的平台上生成存根文件则将遇到一些问题例如在 Web 服务描述语言 (WSDL) 文件中Vector
或其他 Collection 类型映射到 ArrayOfAnyType
其他平台可能不知道将其映射到哪个 Collection 类型而且 Vector
中包含的数据元素也映射到 WSDL 中的 AnyType
(这里存在的一个大问题是其他的平台不知道 AnyType
代表什么类型)有关该主题的详细信息请参阅参考资料中的 Web services programming tips and tricks: Improve the interoperability between JEE and NET
使用数组的最后一个原因是移动 Web 服务不支持 Java Collection 类型这使得所有其他的解释都显得没有必要这意味着您可能无法从形式良好的 WSDL 文件为移动 Web 服务生成存根文件
移动 Web 服务中的首选的一些数据类型
使用基元类型 long
传输 Date 或 Calendar 表示形式
对于标准 JAXRPC 运行时实现有两种支持的标准类型映射
Java 类型到 XML 数据类型
XML 数据类型到 Java 类型
在 JAXRPC 子集规范中只需要第二种映射表 显示了从支持的 XML 数据类型到 Java 类型的映射的简要列表有关详细信息请参阅 JSR
表 从 XML 数据类型到 Java 类型的映射
简单 XML 类型 | Java 类型 | xsd:string
javalangString
xsd:int
int
xsd:long
long
xsd:short
short
xsd:boolean
boolean
xsd:byte
byte
xsd:float
javalangString
或
float
xsd:double
javalangString
或
double
xsd:QName
javaxxmlnamespaceQName
xsd:baseBinary
byte[]
xsd:hexBinary
byte[]
从表 中您可以清楚地看出该列表中不存在像 xsd:dateTimexsd:date 或 xsd:time 这样的元素而在标准 JAXRPC 规范中这三个元素确实是映射到 javautilCalendar 的 XML 类型请注意在 JAXRPC 中定义的 Java 据类型映射到 XML 类型的映射中javautilDate 映射到 xsd:dateTime
那么在尝试传输日期或时间表示形式时您应该使用什么?改为使用 long
类型的时间long
类型的日期格式与不同时区的时间表示形式无关并且因为它是基元类型所以比其他类型的 Java 对象更有效
注意 float 和 double 类型的使用
首先需要注意的一点是正如您所知CLDC (Connected Limited Device Configuration) 并没有出于性能的原因而提供 float 和 double 本机类型即使 CLDC 和 CDC 都为其提供了支持那么如果您必须使用针对 CLDC 的 Web 服务您该如何做呢?JSR 为您提供了部分答案
为了在 CLDC 中缺省支持 xsd:float 和 xsd:double实现必须 生成代码来将这些类型映射到 javalangString为了支持为 float 和 double 提供本机支持的配置和平台(CLDC 和 CDC)存根生成器实现也必须 生成代码来将这些类型映射到适当的本机 Java 类型(详细信息请参阅参考资料以获得指向 JSR: JME Web 服务规范的链接)
我将演示一个添加两个 float 数的简单 Web 服务(清单 )
清单 添加两个 float 数
public class TaskWs { public TaskWs() { } /** * Adding two float numbers and return their sum * @param a First number to add * @param b Second number to add * @return The sum of a and b */ public float addTwo(float a float b) { return a + b; }}}
所生成的 WSDL 定义中的 XML 数据类型定义如清单 所示
清单 与清单 对应的 WSDL 定义
<wsdl:types> <schema targetNamespace= xmlns= xmlns:impl= xmlns:intf= xmlns:wsdl= xmlns:xsd=> <element name=addTwoResponse> <complexType> <sequence> <element name=addTwoReturn type=xsd:float/> </sequence> </complexType> </element> <element name=addTwo> <complexType> <sequence> <element name=a type=xsd:float/> <element name=b type=xsd:float/> <element name=b type=xsd:float/> </sequence> </complexType> </element> </schema> </wsdl:types>
对于针对 CLDC 的 Web 服务客户端所生成的存根如清单 所示
清单 为 CLDC 生成的客户端存根
public interface TaskWs extends javarmiRemote { public javalangString addTwo(javalangString _a javalangString _b) throws javarmiRemoteException javaxxmlrpcJAXRPCException;}
所以当调用 CLDC 中的 Web 服务时您必须使用 addTwo() 方法提供两个 String 参数而对于针对平台 CLDC 的 Web 服务客户端所生成的服务接口与清单 中所描述的类似
清单 为 CLDC 生成的客户端存根
public interface TaskWs extends javarmiRemote { public float addTwo(float _a float _b) throws javarmiRemoteException javaxxmlrpcJAXRPCException;}
这将 xsd:float
映射到所生成的客户端存根中的 float 类型看到 CLDC 和 CLDC 之间的不同之处了吗?
在为移动设备开发 Web 服务时请注意 float 和 double 类型因为 CLDC 虚拟机实现无法加载为 CLDC 生成的存根(使用到 float 和 double 的本机映射)同时针对 CLDC 和 CLDC 的 Java Platform Micro Edition (JME) 应用程序的开发人员应该使用到 javalangString
的缺省映射以获得最好可重用性
在处理输入和输出参数时注意可能出现的问题
JSR 指定了以副本的形式传送并以副本的形式创建返回值的所有参数但是当处理数据集合时零数组 (返回零)和空数组 (返回其本身)需要密切关注
我的建议是尽可能地避免使用空数组当处理移动 Web 服务时空数组可能是一个问题
假定您需要返回任务对象数组原始代码如清单 所示
清单 一个简单的值对象
public class SimpleTask { /** * The name of the task */ private String name; /** * The default constructor * */ public SimpleTask() { } /** * @return Returns the name of the task */ public String getName() { return name; } /** * @param name * The name to set */ public void setName(String name) { thisname = name; }}
Web 服务实现如清单 所示
清单 返回值对象的数组的方法
public SimpleTask[] getSimpleTasks(){ SimpleTask[] tasks = null; /* * Your code dealing with DB goes here * * tasks = */ return tasks; }
当生成 Web 服务存根和使用生成的存根测试 Web 服务时本例中的每一个部分都将正常工作但是因为在结束一个阶段之前您需要使用 Jtest 进行完整的代码检查所以当您对代码片段运行 Jtest 时您将看到一条建议Return zerolength arrays instead of null在犹豫片刻之后后您将赞同 Jtest 的建议如果您返回零数组该代码的客户端必须编写额外的代码来检查返回值是否为零(如清单 所示)
清单 用于调用 Web 服务的客户端代码片段
SimpleTask[] tasks = servicegetSimpleTasks(); if(tasks != null){ int length = taskslength; //do something here }
当您将 SimpleTask[] tasks = null;(清单 中的第 行)修改为 SimpleTask[] tasks = new SimpleTasks[]; 时您只需将清单 编写为
SimpleTask[] tasks = servicegetSimpleTasks(); int length = taskslength;
在修改之后您会认为代码逻辑没有更改并再次运行客户端来调用 Web 服务但是现在却引发了异常到目前为止您已经根据 Jtest 的建议做了许多小的修补——您忘记修改了什么——这可能导致需要花额外的时间来努力找到发生错误的原因这个过程真的漫长而乏味
那么问题究竟出在什么地方呢?一般来说对于零对象数组(如 SimpleTask)返回的 SOAP 消息如清单 所示
清单 返回零数组时的 SOAP 消息
<soapenv:Body><p:getSimpleTasksResponse xmlns:p=> <getSimpleTasksReturn xsi:nil=true /></p:getSimpleTasksResponse></soapenv:Body>
对于空数组(如 SimpleTask[] tasks = new SimpleTask[])SOAP 消息如清单 所示
清单 返回空数组时的 SOAP 消息
<soapenv:Body><p:getSimpleTasksResponse xmlns:p=> <getSimpleTasksReturn /> </p:getSimpleTasksResponse> </soapenv:Body>
其不同之处在于 <getSimpleTasksReturn/>
和 <getSimpleTasksReturn xsi:nil = true>
之间图 说明了空数组参数大部分时间是无效的对于自定义的数据类型(包括另一个自定类型的数组)不要将类变量初始化为空数组——相反要将其初始化为零数组尽管所生成的空数组和零数组的 WSDL 定义是相同的
图 根据 JSR 编码零数组参数和空数组参数
结束语
在处理移动 Web 服务时您需要更加谨慎因为移动 Web 服务规范只支持部分 API如果您计划开发移动 Web 服务则当您处理值类型和集合类型时我向您介绍了一些窍门此外我还提供了以下信息
设计前考虑事项例如何时使用 Web 服务以及如何分析使用 Web 服务套接字或消息传递技术之间的利弊
在决定使用 Web 服务后需要考虑的设计事项包括
管理粒度
设计 Web 服务接口
使用 Document/literal 作为编码样式
为什么您应该使用 JavaBeans 组件而不是 EJB 技术作为您的服务提供程序
为什么您应该避免 XML 元素嵌套太深
嵌套和粒度之间的平衡
在使用 JAXRPC 值类型时需要考虑的移动 Web 服务设计事项包括
公共缺省构造器
Setter 和 getter 方法
使用数组类型而不是 Java Collection 类型
确定首选数据类型
处理输入和输出参数
参考资料
学习您可以参阅本文在 developerWorks 全球站点上的 英文原文
Web 服务编程技巧及窍门: 改善 JEE 与 NET 之间的互操作性(developerWorks 年 月)向您介绍在处理标准 Web 服务时如何管理集合数组和原型数据类型
JSRJME Web 服务规范定义了一个可选的包其提供了从 JME 到 Web 服务的标准访问
我应该采用哪一种 WSDL 样式? (developerWorks 年 月)描述了本文所提供的操作模式(以及另外两种模式)RPC/encodedRPC/literalDocument/encodedDocument/literal 和 Document/literal wrapped 模式
Web Services Interoperability Organization (WSI) 是一个开放的行业组织旨在促进跨平台操作系统和编程语言之间的互操作
Tips and tricks: XML does the job(developerWorks 年 月)说明了如何使用 XMLRPC 来定义移动 Web 服务客户端
Crossplatform programming with Java technology and the IBM Web Services Toolkit for Mobile Devices(developerWorks 年 月)帮助确保您的 Java 应用程序在不需要修改的情况下作为尽可能多的平台运行
JSR: Java APIs for XMLbased RPC 讨论了如何使用 JAXRPC 值类型
Using Mobile Devices with the WSTK(developerWorks 年 月)说明了 Web Services Tool Kit for Mobile Devices 如何帮助开发在小型移动设备上使用 Web 服务的应用程序
交付 Web 服务至移动式应用程序(developerWorks 年 月)说明了如何使用支持 JME 的移动设备和 kSOAP 库访问 Web 服务
为移动设备开发 Web 服务客户端 (developerWorks 年 月)指导您完成构建 JME MIDP 设备上的移动 Web 服务客户端的必要步骤
Connected Limited Device Configuration (CLDCJSR 和 JSR)定义了用于资源约束型设备的一组基本 API 和虚拟设备提供了一个功能强大的 Java 平台用于开发与 Mobile Information Device Profile (MIDP) 结合在具有有限内存处理能力和图形功能的设备上运行的应用程序
Best practices for Web services 系列(developerWorks 年 月)详细介绍了 Web 服务设计考虑事项
请查阅 Safari eReference Bookstore以找到许多特定于移动和其他技术的主题
developerWorks Wireless technology 专区专门发布有关基于移动和普及计算的解决方案的各方面的文章
获得产品和技术
Jtest 可以自动完成 Java 单元测试从正在运行的应用程序自动生成 JUnit 测试用例以及测试单个类或大型的复杂应用程序
Web Services Tool Kit for Mobile Devices (alphaWorks) 是成熟的技术——弄清楚该技术的应用领域
关于作者
Shu Fang Rui 毕业于中国上海交通大学她对无线技术和 Web 服务非常感兴趣除了旅行之外她还喜欢从