Android使用XML全攻略(2)

 

Android 是针对移动设备的一种新兴的开源操作系统和 SDK。借助它,您可以创建功能强大的移动应用程序。当您的应用程序可以访问 Web 服务时,其吸引力会大大增加,这意味着您需要使用 Web 语言:XML。在本文中,您将了解在 Android 上使用 XML 的不同方法,以及如何使用它们构建自己的 Android 应用程序。

更加简单的 SAX 解析

Android SDK 提供了一个名称为android.util.Xml的实用类。清单 7 展示了如何使用这个相同的实用类来设置一个 SAX 解析器。

清单 7. Android SAX 解析器

  1. public class AndroidSaxFeedParser extends BaseFeedParser {
  2. public AndroidSaxFeedParser(String feedUrl) {
  3. super(feedUrl);
  4. }
  5. public List<Message> parse() {
  6. RssHandler handler = new RssHandler();
  7. try {
  8. Xml.parse(this.getInputStream(), Xml.Encoding.UTF_8, handler);
  9. } catch (Exception e) {
  10. throw new RuntimeException(e);
  11. }
  12. return handler.getMessages();
  13. }
  14. }

注意,这个类仍然使用了一个标准的 SAX 处理程序,因此您仅仅重用了 清单 7 中所示的 RssHandler。能够重用 SAX 处理程序是非常不错的,但其代码稍微有些复杂。您可以想像,如果需要解析一个更加复杂的 XML 文档,则处理程序可能会带来各种各样的 bug。举例来说,回头看看 清单 6 中的 endElement 方法。注意,在尝试设置属性之前,它检查了 currentMessage 是否为 null。现在,再回头看看 清单 4 中的示例 XML。 注意,ITEM 标记外部有一些 TITLE 和 LINK
标记。这就是使用 null 检查的原因。否则,每一个 TITLE标记 会导致一个 NullPointerException。Android 提供了自己独有的 SAX API(参见 清单 8),它排除了您编写自己的 SAX 处理程序的需要。

清单 8. 经过简化的 Android SAX 解析器

  1. public class AndroidSaxFeedParser extends BaseFeedParser {
  2. public AndroidSaxFeedParser(String feedUrl) {
  3. super(feedUrl);
  4. }
  5. public List<Message> parse() {
  6. final Message currentMessage = new Message();
  7. RootElement root = new RootElement("rss");
  8. final List<Message> messages = new ArrayList<Message>();
  9. Element channel = root.getChild("channel");
  10. Element item = channel.getChild(ITEM);
  11. item.setEndElementListener(new EndElementListener(){
  12. public void end() {
  13. messages.add(currentMessage.copy());
  14. }
  15. });
  16. item.getChild(TITLE).setEndTextElementListener(new EndTextElementListener(){
  17. public void end(String body) {
  18. currentMessage.setTitle(body);
  19. }
  20. });
  21. item.getChild(LINK).setEndTextElementListener(new EndTextElementListener(){
  22. public void end(String body) {
  23. currentMessage.setLink(body);
  24. }
  25. });
  26. item.getChild(DESCRIPTION).setEndTextElementListener(new
  27. EndTextElementListener(){
  28. public void end(String body) {
  29. currentMessage.setDescription(body);
  30. }
  31. });
  32. item.getChild(PUB_DATE).setEndTextElementListener(new EndTextElementListener(){
  33. public void end(String body) {
  34. currentMessage.setDate(body);
  35. }
  36. });
  37. try {
  38. Xml.parse(this.getInputStream(), Xml.Encoding.UTF_8,
  39. root.getContentHandler());
  40. } catch (Exception e) {
  41. throw new RuntimeException(e);
  42. }
  43. return messages;
  44. }
  45. }

新的SAX 解析代码并未使用 SAX 处理程序,而是使用了 SDK 中的 android.sax 包中的类。这些类允许您构建 XML 文档的结构,并根据需要添加事件监听程序。在以上代码中,您声明文档将有一个 rss 根元素,并且它有一个 channel 子元素。然后,您声明channel 将有一个 ITEM 子元素,并且开始添加监听程序。对于每个监听程序,您都使用了一个实现了特定接口(EndElementListner或 EndTextElementListener)的匿名内部类。注意,您不需要跟踪字符数据。不仅仅因为这样会更加简单,更重要的是更加高效。最后,在调
用 Xml.parse 实用方法时,您将传递一个通过根元素生成的处理程序。

清单 8 中的所有代码都是可选的。如果您习惯 Java 环境中的标准 SAX 解析代码,那么您可以坚持使用它。如果您希望尝试 Android SDK 所提供的便捷的包装器,那么也可以使用它。如果您完全不希望使用 SAX 会怎样呢?可以使用一些备选方案。其中的首选方法就是 DOM。

使用 DOM

Android 完全支持 DOM 解析,就像在桌面机器或服务器上使用 Java 代码运行它一样。清单 9 显示了一个基于 DOM 的解析器接口实现。

清单 9. 基于 DOM 的提要解析器实现

  1. public class DomFeedParser extends BaseFeedParser {
  2. protected DomFeedParser(String feedUrl) {
  3. super(feedUrl);
  4. }
  5. public List<Message> parse() {
  6. DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  7. List<Message> messages = new ArrayList<Message>();
  8. try {
  9. DocumentBuilder builder = factory.newDocumentBuilder();
  10. Document dom = builder.parse(this.getInputStream());
  11. Element root = dom.getDocumentElement();
  12. NodeList items = root.getElementsByTagName(ITEM);
  13. for (int i=0;i<items.getLength();i++){
  14. Message message = new Message();
  15. Node item = items.item(i);
  16. NodeList properties = item.getChildNodes();
  17. for (int j=0;j<properties.getLength();j++){
  18. Node property = properties.item(j);
  19. String name = property.getNodeName();
  20. if (name.equalsIgnoreCase(TITLE)){
  21. message.setTitle(property.getFirstChild().getNodeValue());
  22. } else if (name.equalsIgnoreCase(LINK)){
  23. message.setLink(property.getFirstChild().getNodeValue());
  24. } else if (name.equalsIgnoreCase(DESCRIPTION)){
  25. StringBuilder text = new StringBuilder();
  26. NodeList chars = property.getChildNodes();
  27. for (int k=0;k<chars.getLength();k++){
  28. text.append(chars.item(k).getNodeValue());
  29. }
  30. message.setDescription(text.toString());
  31. } else if (name.equalsIgnoreCase(PUB_DATE)){
  32. message.setDate(property.getFirstChild().getNodeValue());
  33. }
  34. }
  35. messages.add(message);
  36. }
  37. } catch (Exception e) {
  38. throw new RuntimeException(e);
  39. }
  40. return messages;
  41. }
  42. }

与第一个 SAX 示例类似,以上代码完全没有特定于 Android 的地方。DOM 解析器将所有 XML 文档读取到内存中,然后允许您使用 DOM API 遍历 XML 树、检索所需的数据。这是非常直观的代码,并且,在某些方面比基于 SAX 的实现更加简单。但是,DOM 通常更加占用内存,因为一切内容都会先读取到内存中。这对于运行 Android 的移动设备来说是一个问题,但是当 XML 文档始终保持很小的大小时是可行的。这可能意味着,Android 的开发人员会认为 SAX 解析在 Android 应用程序上更加常见,因此为它提供了额外的实用工具。Android
还提供了另一种类型的 XML 解析器,它就是 pull 解析器。

XML pull 解析器

如前所述,Android 并未提供对 Java StAX API 的支持。但是,Android 确实附带了一个 pull 解析器,其工作方式类似于 StAX。它允许您的应用程序代码从解析器中获取事件,这与 SAX 解析器自动将事件推入处理程序相反。清单 10 显示了提要解析接口的一个 pull 解析器实现。

清单 10. 基于 Pull 解析器的实现

  1. public class XmlPullFeedParser extends BaseFeedParser {
  2. public XmlPullFeedParser(String feedUrl) {
  3. super(feedUrl);
  4. }
  5. public List<Message> parse() {
  6. List<Message> messages = null;
  7. XmlPullParser parser = Xml.newPullParser();
  8. try {
  9. // auto-detect the encoding from the stream
  10. parser.setInput(this.getInputStream(), null);
  11. int eventType = parser.getEventType();
  12. Message currentMessage = null;
  13. boolean done = false;
  14. while (eventType != XmlPullParser.END_DOCUMENT && !done){
  15. String name = null;
  16. switch (eventType){
  17. case XmlPullParser.START_DOCUMENT:
  18. messages = new ArrayList<Message>();
  19. break;
  20. case XmlPullParser.START_TAG:
  21. name = parser.getName();
  22. if (name.equalsIgnoreCase(ITEM)){
  23. currentMessage = new Message();
  24. } else if (currentMessage != null){
  25. if (name.equalsIgnoreCase(LINK)){
  26. currentMessage.setLink(parser.nextText());
  27. } else if (name.equalsIgnoreCase(DESCRIPTION)){
  28. currentMessage.setDescription(parser.nextText());
  29. } else if (name.equalsIgnoreCase(PUB_DATE)){
  30. currentMessage.setDate(parser.nextText());
  31. } else if (name.equalsIgnoreCase(TITLE)){
  32. currentMessage.setTitle(parser.nextText());
  33. }
  34. }
  35. break;
  36. case XmlPullParser.END_TAG:
  37. name = parser.getName();
  38. if (name.equalsIgnoreCase(ITEM) &&
  39. currentMessage != null){
  40. messages.add(currentMessage);
  41. } else if (name.equalsIgnoreCase(CHANNEL)){
  42. done = true;
  43. }
  44. break;
  45. }
  46. eventType = parser.next();
  47. }
  48. } catch (Exception e) {
  49. throw new RuntimeException(e);
  50. }
  51. return messages;
  52. }
  53. }

pull 解析器的运行方式与 SAX 解析器相似。它提供了类似的事件(开始元素和结束元素),但您需要使用 (parser.next() 提取它们。事件将作为数值代码被发送,因此您可以使用一个简单 case-switch。注意,解析并未像 SAX 解析那样监听元素的结束,而是在开始处完成了大部分处理。在 清单 10 的代码中,当某个元素开始时,您可以调用 parser.nextText() 从 XML 文档中提取所有字符数据。还需注意,您设置了一个标记(布尔变量 done)来确定何时到达感兴趣内容的结束部分。这允许您提早停止读取
XML 文档,因为您知道代码将不会关心文档的其余部分。这有时非常实用,特别是当您只需要访问一小部分 XML 文档时。通过尽快停止解析,您可以极大地减少解析时间。这种优化对于连接速度较慢的移动设备尤为重要。pull 解析器可以提供一些性能优势以及易用性。它还可以用于编写 XML。

创建 XML

目前为止,我一直专注于通过 Internet 解析 XML。但是,有时您的应用程序可能需要将 XML 发送到远程服务器。显然,您可以只使用一个 StringBuilder 来创建 XML 字符串。另一种备选方法来自 清单 11 中的 Pull 解析器。

清单 11. 使用 pull 解析器编写 XML

  1. private String writeXml(List<Message> messages){
  2. XmlSerializer serializer = Xml.newSerializer();
  3. StringWriter writer = new StringWriter();
  4. try {
  5. serializer.setOutput(writer);
  6. serializer.startDocument("UTF-8", true);
  7. serializer.startTag("", "messages");
  8. serializer.attribute("", "number", String.valueOf(messages.size()));
  9. for (Message msg: messages){
  10. serializer.startTag("", "message");
  11. serializer.attribute("", "date", msg.getDate());
  12. serializer.startTag("", "title");
  13. serializer.text(msg.getTitle());
  14. serializer.endTag("", "title");
  15. serializer.startTag("", "url");
  16. serializer.text(msg.getLink().toExternalForm());
  17. serializer.endTag("", "url");
  18. serializer.startTag("", "body");
  19. serializer.text(msg.getDescription());
  20. serializer.endTag("", "body");
  21. serializer.endTag("", "message");
  22. }
  23. serializer.endTag("", "messages");
  24. serializer.endDocument();
  25. return writer.toString();
  26. } catch (Exception e) {
  27. throw new RuntimeException(e);
  28. }
  29. }

XmlSerializer 类是 前一部分 所使用的 XmlPullParser 包的一部分。它没有提取事件,而是将它们推出到数据流或编写程序中。在本例中,它仅仅将事件推送到了一个 java.io.StringWriter 实例中。它提供了一个直观的 API,通过各种方法开始和结束文档、处理元素以及添加文本或属性。这是StringBuilder的一种出色的替换方案,因为它可以更加轻松地确保您的 XML 具有良好结构。

结束语

您希望为Android设备构建何种类型的应用程序?无论如何,如果它需要通过 Internet 使用数据,那么都可能需要使用XML。在本文中,您看到 Android 提供了大量用于处理XML的工具。您可以选择其中之一作为自己的工具,或者您可以根据用例来进行选择。大多数时间,使用SAX是比较安全的,并且Android提供了一种传统的SAX 使用方法,以及一个便捷的SAX包装器。如果您的文档比较小,那么DOM可能是一种比较简单的方法。如果您的文档比较大,但您只需要文档的一部分,则XML Pull解析器可能是更为有效的方法。最后,对于编写XML,Pull解析器包也提供了一种便捷的方法。因此,无论您的XML需求如何,Android都能在一定程度上满足它们。

Android使用XML全攻略(2)的更多相关文章

  1. Android使用XML全攻略(1)

    Android使用XML全攻略(1)    Android 是针对移动设备的一种新兴的开源操作系统和 SDK.借助它,您可以创建功能强大的移动应用程序.当您的应用程序可以访问 Web 服务时,其吸引力 ...

  2. Android屏幕适配全攻略(最权威的官方适配指导)屏幕尺寸 屏幕分辨率 屏幕像素密度 dpdipdpisppx mdpihdpixdpixxdpi

    Android屏幕适配全攻略(最权威的官方适配指导)原创赵凯强 发布于2015-05-19 11:34:17 阅读数 153734 收藏展开 转载请注明出处:http://blog.csdn.net/ ...

  3. Android屏幕适配全攻略 (转载)

    http://blog.csdn.net/jdsjlzx/article/details/45891551 https://github.com/hongyangAndroid/AndroidAuto ...

  4. 【收藏】Android屏幕适配全攻略(最权威的Google官方适配指导)

    来源:http://blog.csdn.net/zhaokaiqiang1992 更多:Android AutoLayout全新的适配方式, 堪称适配终结者 Android的屏幕适配一直以来都在折磨着 ...

  5. Android屏幕适配全攻略(最权威的官方适配指导)(转),共大家分享。

    Android的屏幕适配一直以来都在折磨着我们这些开发者,本篇文章以Google的官方文档为基础,全面而深入的讲解了Android屏幕适配的原因.重要概念.解决方案及最佳实践,我相信如果你能认真的学习 ...

  6. Android屏幕适配全攻略(最权威的官方适配指导) (转)

    招聘信息: Cocos2d-X 前端主程 [新浪微博]手机客户端iOS研发工程师 20k-40k iOS 开发工程师 iOS高级开发工程师(中国排名第一的企业级移动互联网云计算公司 和创科技 红圈营销 ...

  7. 【转】Android屏幕适配全攻略(最权威的官方适配指导)

    原文网址:http://blog.csdn.net/jdsjlzx/article/details/45891551 Android的屏幕适配一直以来都在折磨着我们这些开发者,本篇文章以Google的 ...

  8. Android屏幕适配全攻略(最权威的官方适配指导)

    转载请注明出处:http://blog.csdn.net/zhaokaiqiang1992 Android的屏幕适配一直以来都在折磨着我们这些开发者,本篇文章以Google的官方文档为基础,全面而深入 ...

  9. Android屏幕适配全攻略(最权威的官方适配指导)

    摘自:http://www.cocoachina.com/android/20151030/13971.html Android屏幕适配出现的原因 在我们学习如何进行屏幕适配之前,我们需要先了解下为什 ...

随机推荐

  1. vijos 1047 送给圣诞夜的礼品 矩阵

    题目链接 描述 当小精灵们把贺卡都书写好了之后.礼品准备部的小精灵们已经把所有的礼品都制作好了.可是由于精神消耗的缘故,他们所做的礼品的质量越来越小,也就是说越来越不让圣诞老人很满意.可是这又是没有办 ...

  2. Sencha Touch 之 Ext.fly方法的使用

    Ext.fly方法是Ext.js 4中的flyweight技术,该技术在浏览器中为使用Ext.fly方法的元素节点开辟一块内存,下一次使用Ext.fly方法的元素节点将占据同一块内存,即覆盖前一次的元 ...

  3. MySQLdb的安装

    第一步:下载安装介质 https://pypi.python.org/pypi/MySQL-python 注意虽然模块名叫MySQLdb但是MySQL-python指的就是MySQLdb 第二步:安装 ...

  4. JAVA工具_PinyinConv

    package cn.chh.utils; /** * 获得每个字符的首字母 * @author CHH * @since 2013-01-21 * @bugs 不支持多音字处理 */ public ...

  5. delphi写的整合汇编与api的简单的窗口程序

    program Project1; { Types and Structures Definition }type  WNDCLASSEX = packed record    cbSize: Lon ...

  6. Android程序的入口点

    原文:Android程序的入口点 android应用程序,由一到多个Activity组成.每个Activity没有很紧密的联系,因为我们可以在自己的程序中调用其它Activity,特别是调用自己的代码 ...

  7. [置顶] Android系统移植与调试之------->如何修改Android设备状态条上音量加减键在横竖屏的时候的切换与显示

    这两天由于一个客户的要求,将MID竖屏时候的状态条上的音量键去掉.所以尝试修改了一下,成功了,分享一下经验. 先看一下修改后的效果图,如下所示 . 横屏的时候:有音量加减键 竖屏的时候:音量加减键被去 ...

  8. JS isArray记录

    var isArray=Function.isArray||function(0){ return typeof o === "object" && Object. ...

  9. poj2186 Popular Cows --- 强连通

    给一个有向图,问有多少结点是其它全部结点都能够到达的. 等价于,在一个有向无环图上,找出度为0 的结点.假设出度为0的结点仅仅有一个,那么这个就是答案.假设大于1个.则答案是0. 这题有环.所以先缩点 ...

  10. Mongodb数据库命令端经常使用操作

    数据库基本命令操作 数据库经常使用命令 1.Help查看命令提示 help db.help(); db.yourColl.help(); db.youColl.find().help(); rs.he ...