Java ME平台下的Web服务访问指南
1. 引言
随着移动设备的普及和技术的进步,越来越多的应用程序需要与网络服务进行交互。Java ME平台提供了丰富的API和工具,使得开发人员能够在资源受限的设备上高效地使用Web服务。本篇文章将深入探讨如何从Java ME平台访问Web服务,涵盖从客户端视角审视Web服务、数据交换、XML表示、XML支持、XML生成、以及解析工具等多个方面。通过这些内容,读者可以学习到如何在Java ME设备上有效地使用Web服务。
2. 从客户端视角审视Web服务
网络服务是客户端/服务器计算模型的一种应用,使用HTTP作为承载协议,并使用XML、JSON或YAML等语言表示数据。一个典型的Web服务架构包括三个关键组成部分:服务代理(Service Broker)、服务提供者(Service Providers)和服务请求者(Service Requestors)。服务请求者通过服务代理找到合适的服务提供者,并与其进行交互。
2.1 Web服务架构的三个组成部分
- 服务代理 :负责帮助客户端确定一个或多个服务提供者的位置。
- 服务提供者 :负责提供远程计算设施,如计算和对象交换。
- 服务请求者 :使用服务代理找到服务提供者并与之互动的客户端。
3. 在网络上交换数据
在编写Web服务客户端时,HTTP或HTTPS是最常用的协议。HTTP是一个轻量级的协议,适用于数据交换,但安全性较低。因此,对于需要安全性的应用,如电子商务,通常使用HTTPS。此外,编写Web服务客户端时,还需考虑使用的HTTP方法,尤其是使用REST时,HTTP方法提供了关于Web服务操作的语义。
3.1 使用HTTP方法
REST范式依赖于HTTP方法来提供关于Web服务操作的语义。例如,删除消息时可能需要发送一个带有
DELETE
方法的HTTP请求。这可以通过
HttpConnection
的
setRequestMethod
方法实现。
try {
HttpConnection hc = (HttpConnection) Connector.open(url);
hc.setRequestMethod("DELETE");
// 其他操作
} catch (Exception e) {
// 错误处理
}
3.2 处理Cookies
在与服务器交互时,处理Cookies是非常重要的。获取Cookie后,应将其拆分为会话ID和域部分,并在后续请求中检查域名是否匹配。
String cookie = hc.getHeaderField("Set-Cookie");
if (cookie != null) {
int semicolon = cookie.indexOf(';');
String sessionId = cookie.substring(0, semicolon);
}
// 发送Cookie
HttpConnection hc = (HttpConnection) Connector.open(url);
hc.setRequestProperty("Cookie", sessionId);
4. 使用XML进行数据表示
XML是一种广泛使用的标记语言,提供了标准的语法来创建自定义的标记语言,允许你描述文档中的数据。在Java ME中,通常会创建和操作结构良好的XML内容,而将验证工作交给服务器。
4.1 XML文档的有效性
XML文档分为两类:结构良好的文档(Well-formed Documents)和有效的文档(Valid Documents)。结构良好的文档满足XML本身的句法要求,而有效文档还满足用户或其他XML文档语义表示(如XML模式或DTD)所施加的语义约束。
4.2 示例:天气报告的XML表示
<wx:weather>
<wx:wind units="MPH">
<wx:direction units="compass">ENE</wx:direction>
<wx:speed>5</wx:speed>
</wx:wind>
<wx:precipitation/>
<wx:text>
<wx:when time="today">
Mostly cloudy in the morning then becoming mostly sunny.
Patchy fog in the morning. Highs in the 70s to lower 80s.
Afternoon seabreeze 10 to 20 mph.
</wx:when>
<wx:when time="tonight">
Mostly clear except areas of low clouds and fog developing overnight.
Lows in the mid to upper 50s. Evening seabreeze 10 to 20 mph.
</wx:when>
</wx:text>
</wx:weather>
5. Java ME中对Web服务的XML支持
在Java ME中使用Web服务通常涉及以下步骤:
- 应用程序展示用户界面。
- 用户或应用程序采取某些操作,触发向远程Web服务发出请求。
- 应用程序生成一个XML文档,代表Web服务请求(通常称为请求的marshalling数据)。
- 使用HTTP,应用程序通过POST请求将XML文档传输到远程Web服务。
- 使用HTTP,应用程序从远程Web服务接收响应XML文档。
- 应用程序解析生成的XML文档,以获取应用程序或用户感兴趣的数据(通常称为响应的unmarshalling)。
- 应用程序使用解析后的数据。
5.1 使用XML生成和解析
Java ME中的XML生成通常采用两种形式之一:使用模板组件和
StringBuffer
类进行简单拼接,或使用支持SOA的J2ME Web服务规范。解析XML可以通过两种方式实现:依赖可选的Web服务规范或包含轻量级解析器如kXML。
| 方法 | 描述 |
|---|---|
使用模板组件和
StringBuffer
| 简单但繁琐,逐步构建XML文档。 |
| 使用J2ME Web服务规范 | 支持SOA,更适合复杂应用。 |
5.2 示例:生成XML表示
public String toXml() {
StringBuffer xmlBuffer = new StringBuffer();
xmlBuffer.append(XML_PREAMBLE);
xmlBuffer.append(XML_START_OPEN);
xmlBuffer.append(XML_TAG_WEATHER);
if (city != null) {
xmlBuffer.append(" ");
xmlBuffer.append(XML_ATTR_CITY);
xmlBuffer.append(XML_ATTR_IS);
xmlBuffer.append(XML_ATTR_QUOTE);
xmlBuffer.append(city);
xmlBuffer.append(XML_ATTR_QUOTE);
}
if (state != null) {
xmlBuffer.append(" ");
xmlBuffer.append(XML_ATTR_STATE);
xmlBuffer.append(XML_ATTR_IS);
xmlBuffer.append(XML_ATTR_QUOTE);
xmlBuffer.append(state);
xmlBuffer.append(XML_ATTR_QUOTE);
}
if (country != null) {
xmlBuffer.append(" ");
xmlBuffer.append(XML_ATTR_COUNTRY);
xmlBuffer.append(XML_ATTR_IS);
xmlBuffer.append(XML_ATTR_QUOTE);
xmlBuffer.append(country);
xmlBuffer.append(XML_ATTR_QUOTE);
}
if (forecast != null || temp != null ||
(precipitation != null && precipitationProb != null && precipitationType != null) ||
(windSpeed != null && windDirection != null)) {
xmlBuffer.append(XML_END_OPEN);
xmlBuffer.append(XML_NEWLINE);
if (temp != null) {
xmlBuffer.append(XML_START_OPEN);
xmlBuffer.append(XML_TAG_TEMPS);
xmlBuffer.append(XML_ATTR_TUNITS);
xmlBuffer.append(XML_END_OPEN);
xmlBuffer.append(XML_TAG_TEMPS);
xmlBuffer.append(XML_ATTR_TUNITS);
xmlBuffer.append(XML_END_OPEN);
xmlBuffer.append(XML_NEWLINE);
xmlBuffer.append(XML_START_OPEN);
xmlBuffer.append(XML_TAG_TEMP);
xmlBuffer.append(" ");
xmlBuffer.append(XML_ATTR_TYPE);
xmlBuffer.append(XML_ATTR_IS);
xmlBuffer.append("\"current\"");
xmlBuffer.append(XML_END_OPEN);
xmlBuffer.append(temp);
xmlBuffer.append(XML_START_CLOSE);
xmlBuffer.append(XML_TAG_TEMP);
xmlBuffer.append(XML_END_CLOSE);
xmlBuffer.append(XML_NEWLINE);
xmlBuffer.append(XML_START_CLOSE);
xmlBuffer.append(XML_TAG_TEMPS);
xmlBuffer.append(XML_END_CLOSE);
xmlBuffer.append(XML_NEWLINE);
}
if (windSpeed != null && windDirection != null) {
xmlBuffer.append(XML_START_OPEN);
xmlBuffer.append(XML_TAG_WIND);
}
}
return xmlBuffer.toString();
}
6. Java ME中的XML解析器
Java ME中的XML解析器分为三种类型:DOM解析器、Push解析器和Pull解析器。DOM解析器解析整个文档并返回文档树的实例,但通常不用于Java ME环境。Push解析器调用客户端接口的方法,将数据“推送”给客户端。Pull解析器则相反,代码扫描XML并调用解析器的方法获取标签和其他信息。
6.1 示例:使用kXML解析器
kXML是一个轻量级的XML解析器,适合在Java ME环境中使用。要使用kXML解析器,需下载实现kXML解析器的JAR文件,并将其包含在NetBeans项目中。
public void parse(ByteArrayInputStream in) {
try {
InputStreamReader reader = new InputStreamReader(in);
KXmlParser parser = new KXmlParser();
boolean keepParsing = true;
parser.setInput(reader);
while (keepParsing) {
int type = parser.next();
switch (type) {
case XmlPullParser.START_TAG:
// 处理开始标签
break;
case XmlPullParser.END_TAG:
// 处理结束标签
break;
case XmlPullParser.TEXT:
// 处理文本内容
break;
default:
break;
}
}
} catch (Exception e) {
System.out.println(e.toString());
e.printStackTrace();
}
}
接下来,我们将继续探讨J2ME Web服务规范和kXML解析器的具体应用。
7. J2ME Web服务规范简介
J2ME Web服务规范(JSR 172)为Java ME应用程序提供了与Web服务交互的标准方法。该规范包括JAX-RPC子集和XML解析器,使得Java ME应用程序能够与基于SOAP的Web服务进行通信,并处理XML数据。以下是使用J2ME Web服务规范的关键步骤:
- 在编译时 ,根据服务的WSDL描述定义并生成存根。
- 在运行时 ,实例化存根的一个实例。
- 在运行时 ,调用存根上的方法,这些方法对应于服务端点的操作实现。
- 打包生成的存根 ,与Java ME客户端应用程序一起发布。
7.1 JAX-RPC接口
JAX-RPC接口提供了Java接口与基于Web的RPC服务的接口。这个子集支持Java与SOAP之间的基本类型(如boolean、byte、short、int、long、String、数组和复杂类型)的序列化与反序列化。由于平台限制,这不是JAX-RPC的完整实现,但足以满足大多数Java ME应用程序的需求。
7.2 示例:使用JAX-RPC接口
// 编译时生成存根
// 运行时实例化存根
MyWebServiceStub stub = new MyWebServiceStub();
// 调用服务方法
stub.myServiceMethod();
8. 使用kXML解析器
如前所述,kXML是一个轻量级的XML解析器,非常适合在Java ME环境中使用。它提供了拉取式解析器,允许开发者逐个处理XML标记,而不是依赖事件驱动的解析方式。以下是kXML解析器的具体使用步骤:
- 下载并导入kXML库。
-
创建
KXmlParser实例。 - 设置输入流。
-
使用
next方法逐个读取XML标记。 - 根据需要处理不同的XML事件。
8.1 示例:使用kXML解析器
public void parse(ByteArrayInputStream in) {
try {
InputStreamReader reader = new InputStreamReader(in);
KXmlParser parser = new KXmlParser();
parser.setInput(reader);
while (true) {
int eventType = parser.next();
switch (eventType) {
case XmlPullParser.START_TAG:
String tagName = parser.getName();
// 处理开始标签
break;
case XmlPullParser.END_TAG:
// 处理结束标签
break;
case XmlPullParser.TEXT:
String text = parser.getText();
// 处理文本内容
break;
default:
break;
}
}
} catch (Exception e) {
System.out.println(e.toString());
e.printStackTrace();
}
}
8.2 kXML解析器的优势
- 轻量级 :kXML解析器的体积较小,适合资源受限的Java ME设备。
- 灵活性 :拉取式解析器允许开发者根据需要逐个处理XML标记,提高了代码的灵活性。
- 性能 :相比于事件驱动的解析方式,拉取式解析器通常具有更好的性能表现。
9. 实战案例:WeatherFetcher
为了更好地理解如何在Java ME应用程序中使用Web服务,我们来看一个具体的实战案例——WeatherFetcher。该应用程序通过RESTful Web服务获取天气数据,并将其解析为Java对象。
9.1 WeatherFetcher的工作流程
- 创建URL :根据用户提供的位置信息,构造Web服务请求的URL。
-
发起HTTP请求
:使用
HttpConnection发起GET请求。 - 读取响应 :将服务器返回的XML数据读取到字符串中。
- 解析XML :使用kXML解析器解析XML数据,并将其转换为Java对象。
- 更新UI :将解析后的天气数据更新到用户界面上。
9.2 示例代码:WeatherFetcher
package com.apress.rischpater.weatherwidget;
import java.io.*;
import javax.microedition.io.*;
public class WeatherFetcher implements Runnable {
private static String url = "http://www.noplace.com/location/";
private Location location;
private boolean cancelled;
private WeatherWidget app;
public WeatherFetcher(Location l, WeatherWidget a) {
location = l;
app = a;
cancelled = false;
if (l != null && a != null) {
Thread thread = new Thread(this);
thread.start();
}
}
public void cancel() {
cancelled = true;
}
private static String urlEncode(String s) {
if (s != null) {
StringBuffer tmp = new StringBuffer();
int i = 0;
try {
while (true) {
int b = (int) s.charAt(i++);
if ((b >= 0x30 && b <= 0x39) || (b >= 0x41 && b <= 0x5A) || (b >= 0x61 && b <= 0x7A)) {
tmp.append((char) b);
} else {
tmp.append("%");
if (b <= 0xf) tmp.append("0");
tmp.append(Integer.toHexString(b));
}
}
} catch (Exception e) {}
return tmp.toString();
}
return null;
}
@Override
public void run() {
try {
String requestUrl = url + urlEncode(location.getCity()) + "," + urlEncode(location.getState()) + "," + urlEncode(location.getCountry());
HttpConnection hc = (HttpConnection) Connector.open(requestUrl);
hc.setRequestMethod("GET");
InputStream is = hc.openInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) != -1) {
baos.write(buffer, 0, length);
}
String xml = new String(baos.toByteArray());
// 解析XML
LocationParser parser = new LocationParser();
byte[] xmlBytes = xml.getBytes();
ByteArrayInputStream bis = new ByteArrayInputStream(xmlBytes);
parser.parse(bis);
// 更新UI
app.updateWeather(parser.getLocation());
} catch (Exception e) {
// 错误处理
}
}
}
9.3 XML解析器:LocationParser
package com.apress.rischpater.weatherwidget;
import org.kxml2.io.KXmlParser;
import java.io.InputStream;
public class LocationParser {
private Location location;
private boolean setForecast;
private boolean hasForecast;
private boolean setTemp;
private String precipitation, precipType, precipProb;
private String windSpeed, windDirection;
private StringBuffer buffer;
public LocationParser() {
location = new Location();
}
public Location getLocation() {
return location;
}
public void parse(InputStream in) {
try {
KXmlParser parser = new KXmlParser();
parser.setInput(new InputStreamReader(in));
while (true) {
int eventType = parser.next();
switch (eventType) {
case XmlPullParser.START_TAG:
String tagName = parser.getName();
if ("city".equals(tagName)) {
location.setCity(parser.nextText());
} else if ("state".equals(tagName)) {
location.setState(parser.nextText());
} else if ("country".equals(tagName)) {
location.setCountry(parser.nextText());
} else if ("temperature".equals(tagName)) {
location.setTemperature(parser.getAttributeValue(null, "value"));
} else if ("wind".equals(tagName)) {
windSpeed = parser.getAttributeValue(null, "speed");
windDirection = parser.getAttributeValue(null, "direction");
} else if ("precipitation".equals(tagName)) {
precipitation = parser.getAttributeValue(null, "probability");
precipType = parser.getAttributeValue(null, "type");
}
break;
case XmlPullParser.END_TAG:
// 处理结束标签
break;
case XmlPullParser.TEXT:
// 处理文本内容
break;
default:
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
10. 总结与最佳实践
在Java ME平台上访问Web服务是一项复杂的任务,但通过合理利用现有的API和工具,可以大大简化这一过程。以下是几个关键的最佳实践:
- 选择合适的协议 :根据需求选择HTTP或HTTPS,确保数据传输的安全性。
- 优化HTTP方法 :特别是在使用REST时,选择适当的HTTP方法以提高效率。
- 使用轻量级解析器 :如kXML,避免使用DOM解析器以节省资源。
- 处理异常情况 :确保代码中包含足够的异常处理机制,以应对网络不稳定等常见问题。
通过遵循这些最佳实践,开发者可以在Java ME平台上构建高效、稳定的Web服务客户端应用程序。希望这篇文章能帮助你在Java ME平台上更好地理解和应用Web服务。
通过上述内容,我们已经全面探讨了如何在Java ME平台上访问Web服务。从客户端视角审视Web服务、数据交换、XML表示、XML支持、XML生成,到具体的解析工具如kXML,每一个环节都至关重要。希望这些内容能为你的Java ME开发之旅提供有价值的参考。
超级会员免费看
1024

被折叠的 条评论
为什么被折叠?



