12、Java ME平台下的Web服务访问指南

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服务架构的三个组成部分

  • 服务代理 :负责帮助客户端确定一个或多个服务提供者的位置。
  • 服务提供者 :负责提供远程计算设施,如计算和对象交换。
  • 服务请求者 :使用服务代理找到服务提供者并与之互动的客户端。

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服务通常涉及以下步骤:

  1. 应用程序展示用户界面。
  2. 用户或应用程序采取某些操作,触发向远程Web服务发出请求。
  3. 应用程序生成一个XML文档,代表Web服务请求(通常称为请求的marshalling数据)。
  4. 使用HTTP,应用程序通过POST请求将XML文档传输到远程Web服务。
  5. 使用HTTP,应用程序从远程Web服务接收响应XML文档。
  6. 应用程序解析生成的XML文档,以获取应用程序或用户感兴趣的数据(通常称为响应的unmarshalling)。
  7. 应用程序使用解析后的数据。

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服务规范的关键步骤:

  1. 在编译时 ,根据服务的WSDL描述定义并生成存根。
  2. 在运行时 ,实例化存根的一个实例。
  3. 在运行时 ,调用存根上的方法,这些方法对应于服务端点的操作实现。
  4. 打包生成的存根 ,与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解析器的具体使用步骤:

  1. 下载并导入kXML库。
  2. 创建 KXmlParser 实例。
  3. 设置输入流。
  4. 使用 next 方法逐个读取XML标记。
  5. 根据需要处理不同的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的工作流程

  1. 创建URL :根据用户提供的位置信息,构造Web服务请求的URL。
  2. 发起HTTP请求 :使用 HttpConnection 发起GET请求。
  3. 读取响应 :将服务器返回的XML数据读取到字符串中。
  4. 解析XML :使用kXML解析器解析XML数据,并将其转换为Java对象。
  5. 更新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开发之旅提供有价值的参考。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值