登录 立即注册
安币:

查看: 887015|回复: 102

构建 Android 手机 RSS 阅读器

  [复制链接]

2826

主题

7684

帖子

9420

安币

管理员

Rank: 9Rank: 9Rank: 9

QQ达人最佳新人活跃会员热心会员推广达人灌水之王突出贡献

QQ
发表于 2011-9-17 13:02:21 | 显示全部楼层 |阅读模式
      如果不能随时了解最喜欢的新闻提要,那么移动计算环境又有什么用呢?当然,可以使用 Android 的浏览器浏览最喜爱的 Web 站点,但遗憾的是,这些站点很可能没有针对两英寸高的屏幕进行优化。此外,您也无法使用其他移动应用程序集成 RSS 或其他 XML 数据以构建自己的 mash-up。本教程将展示如何使用 Android Developer Tools 读取、解析和显示 XML 数据。如果您想在 Eclipse 环境中开始开发 Android 应用程序,请同时学习教程:“用 Eclipse 开发 Android 应用程序”。该教程介绍了如何在 Eclipse 环境中开发 Android 应用程序。


     开始之前本教程介绍了如何在 Android 平台之上处理 XML。要按照本教程构建样例应用程序,必须在开发计算机中安装和运行 Android SDK。推荐使用 Eclipse 构建 Android 应用程序,但不作硬性要求。具备移动开发经验固然有帮助,但是 Android 应用程序开发要求具有 Java™ 编程技能,这将有助于本教程的学习。关于本教程常用缩写词
  • DOM:文档对象模型(Document Object Model)
  • GUI:图形用户界面(Graphical user interface)
  • HTTP:超文本传输协议(Hyper Text Transport Protocol)
  • RSS:真正简单的连锁(Really Simple Syndication)
  • SAX:XML 简单API(Simple API for XML)
  • SDK:软件开发套件(Software Development Kit)
  • XML:可扩展标记语言(Extensible Markup Language)


Android 的 RSS 阅读器为什么值得我们关注?XML 又是如何融入其中的呢?首先,Android 是一种在移动市场中迅速获得广泛关注的平台。甚至在配备了 Android 的消费型设备出现之前,许多开发人员就已纷纷投入到这一移动市场,希望为即将出现的、备受期待的 Android 式设备做好准备。诸如 Android 这样的富设备(Rich device)要求提供内容。某些情况下,内容的表现形式为游戏或一个生产力应用程序。然而,除了移动电子邮件外,最能够驱动数据使用的内容是新闻和信息。这种内容可能看上去类似于与特定金融市场有关的新闻,或者是天气预报这种比较普遍的信息。对于周末想到海边度假的人,自然十分关心天气预报。于是,以一种简单、实用的方式从移动设备获取数据成为了摆在人们面前的一道难题。Android 包含了 WebKit.org 浏览器引擎,这意味着它可以向移动用户交付高质量的浏览器体验。然而,对于一个小型屏幕和有限的输入设备,在多个 Web 站点中查找新闻和消息是非常乏味的事情。不要误会,自从出现 WAP 浏览器和有限的基于文本的信息以来,这一市场已经得到了长足的发展,然而,移动信息的价值在于:输入 RSS 提要时一眼就可以判断出它是否可用。RSS 是 “Really Simple Syndication” 的缩写。实际上,RSS 是一种分布感兴趣的信息的方便方法。RSS 支持 XML 数据;数据包含完整信息源的简单摘要。如果对这些摘要该兴趣,用户可以进一步获取信息并获得全部内容。本教程将演示在构建 Android RSS 阅读器过程中涉及到的几个主要的 XML 处理步骤。本教程分为以下小节:
  • RSS 基础
  • Android RSS 阅读器应用程序架构
  • 使用 SAX 获取并解析 XML 数据
  • 使用 Android 呈现 RSS 数据




先决条件本教程需要结合使用几种技术。需要通过以下步骤获得所有这些工具。
  • http://www.eclipse.org/downloads 下载 Eclipse。
  • 要安装 Android Developer Tools(Eclipse 插件),请按照 Android 文档 “安装 Android SDK” 中的说明操作。
  • http://code.google.com/android 中下载 Android SDK。Android SDK 是一个不断发展的项目。撰写本教程时 SDK 的最新版本为 m5-rc14。
  • 选择一个 RSS 提要。本教程使用 developerWorks Web 站点中最受欢迎教程的提要。
  • 建立一个活动的 Internet Connection 来运行示例应用程序。
完整的源代码可以从 下载 小节获得。本教程包含的源代码片段包括:
  • AndroidManifest.xml 片段:该文件是 Android 应用程序的应用程序部署描述符。
  • RSSReader:该类实现主 GUI 并包含菜单处理代码。
  • RSSFeed:该类含有一个经过解析的 RSS 提要,包含 RSS 通道信息和一组 RSSItems。
  • RSSHandler:该类实现 SAX 解析器处理程序,从而实现对 XML 流(Internet 中的 RSS 提要)的解析并填充一个 RSSFeed 实例。
  • RSSItem:该类表示解析 XML 流得到的一个 RSS 项。
  • ShowDescription:这是一种行为(Activity),显示所选的 RSSItem 的摘要并包含可链接的文本,可以无缝启动 Android 浏览器来访问 RSSItem 中的可用链接。启用这一 Activity 将在伪同步调用中结合使用一个 Intent 和额外的 Bundle。
  • R.java:该文件表示应用程序使用的 GUI 标识符。
术语构建一个 Android RSS/XML 应用程序需要 XML、RSS 和 HTTP 方面的知识,还需要了解 Android 平台的各个方面。了解下面的术语对于学习本教程大有裨益。要想了解关于这些主题的更多信息,请参见 参考资料 中的链接。
  • Android:Open Handset Alliance 的旗舰产品。这是一个针对手机等移动设备的开源操作环境。
  • RSS:Really Simple Syndication 是一种可以通过高效的方式发布信息的数据格式。
  • XML:eXtensible Mark-up Language 是一种自描述性的数据格式。
  • HTTP:Hyper Text Transport Protocol 几乎专门用来传输所有的 RSS 提要。
  • 解析器(Parser):这个工具的作用是从一种格式中提取信息并使信息可用于其他结构。例如,XML 解析器可以从一个 XML 数据源提取数据。
  • 模拟器(Emulator):这种软件工具代表另一种系统。Android 目前还不能用于真实的用户硬件平台;本文在 Android Emulator 上运行示例应用程序。




    RSS 基础介绍在详细介绍下载 RSS 提要并使用基于 Android 的 RSS 阅读器进行处理之前,让我们首先简要回顾一下使用 RSS 的需求、位置以及它的结构。为什么使用 RSS?信息传播工具多种多样,包括可以免费收听的无线电广播、公共和有线电视、印刷媒体,甚至包括 Internet 这样颠覆性的技术,以及其庞大的 Web 站点和电子邮件订阅。虽然选择很多,但是这些工具都存在一个问题:很难在庞杂的海量数据中精确查找到真正感兴趣的信息和价值。幸运的是,RSS 可以帮助我们解决这个问题。RSS 是 “Really Simple Syndication” 的缩写。RSS 是一种内容发布者用来发布信息的 XML 数据格式,这些信息经过了分类并适合人机阅读。RSS 提要通常使用诸如新闻阅读器这种人类可读的友好格式进行处理并显示给用户,本教程中构建的应用程序就是这样一种新闻阅读器。RSS 提要同样可以供计算机使用,从而生成后续的、聚合的信息源。举例来说,定期检索并提供给旅行社本地预订系统的租赁列表就是一个机器使用 RSS 提要的例子。在讨论新闻内容时经常会提到 RSS,然而,除新闻以外,这种格式可用于组织和发布其他很多类型的信息。例如,很多 Web 站点都发布可用的 pod-cast 的 RSS 提要,从而发布音频和视频内容。Handango.com 是一个流行的移动软件和内容的在线商店,它根据移动平台分类,提供新的流行软件标题的提要。例如,提供针对 Windows® Mobile、Blackberry、Palm 和其他平台的提要。用不了多久,很可能就会提供针对 Android 应用程序的提要了!RSS 的格式是 XML 数据,这表示数据本身就包含描述性元素,也就是说它是自包含的。随着行业的逐步规范化,XML 结构在过去几年也经历了一些变化。最新的版本也是应用最广的版本是 2.0。RSS 2.0 是一种相对简单的 XML 结构,很容易由计算机程序解析。在本教程中,我们将解析从 IBM developerWorks Web 站点获得的一条 RSS 提要。


    RSS version 2.0关于哪一个版本最流行已经进行了太多的讨论,现在让我们了解一下 RSS 2.0 规范。RSS 2.0 格式只包含有限的标记。<?xml> 是一个强制使用的标记,所有 XML 文档都需要使用到。第一个标记实际是 <rss> 标记,它以属性的形式包含版本。<rss> 标记是一个或多个通道,每个通道都包含如清单 1 所示的元素。
清单 1. RSS 2.0 中的通道和元素
  1. <channel>
  2. <title/>
  3. <link/>
  4. <description/>
  5. <pubDate />
  6. <language/>
  7. <copyright/>
  8. <image>
  9. <title />
  10. <url />
  11. <link />
  12. </image>
  13. + <item />
  14. </channel>
  15. </rss>
复制代码
每个通道可能含有一个或多个项。每个项具有如清单 2 所示的结构。
清单 2. 项结构
  1. <item>
  2. <title />
  3. <description />
  4. <link />
  5. <category />
  6. <pubDate />
  7. </item>
复制代码





数据使用RSS 提要的使用者可以决定如何显示数据。通常,RSS 阅读器将显示一些有关可用通道的标题信息,包括发布日期(包含在 pubDate 元素中)。注意,RSS 是用于发布相关的即时数据,因此不应该忽视发布日期。图 1 演示了本教程中运行的示例应用程序,它显示了一条 RSS 提要。图 1 显示了 RSS 提要的标题、发布日期,并使用列表(List)格式显示提要项。本教程后面的内容讨论将解析得到的 RSS 提要提供给 Android 用户界面的机制。
图 1. 运行中的 Android RSS 阅读器

RSS 提要的 item 标记中的元素描述提要中包含的所有项。例如,在本教程中,您要处理的 RSS 提要中包含来自 IBM developerWorks 站点中的一组受欢迎的教程。提要中的每个条目都提供了足够的信息,使用户充分了解提要的主题以及如何获取更多信息。这些信息共分三个级别,每个级别的信息都非常详细。此外,每个条目都包含一个发布日期和类别信息。条目标题简要介绍了提要的主题,例如:<title><![CDATA[Develop Android applications with Eclipse]]></title>。如果您对使用 Eclipse 进行 Android 开发感兴趣的话,那么这个条目标题足够引起您的兴趣。如果用户(即使有应用程序的人员)希望了解更多,则可以获取更多信息,即清单 3 中的描述。
清单 3. 描述
  1. <description><![CDATA[Android is Google's oft-discussed mobile,
  2. wireless, computer, and communications platform. You can take advantage of
  3. the powerful Eclipse environment to build Android applications using the Android
  4. Eclipse plug-in. This tutorial introduces Android application development with the
  5. Eclipse plug-in, otherwise known as Android Development Tools. The tutorial provides
  6. an introduction to Android development with a quick introduction to the platform, a
  7. tour of Android Development Tools, and includes the construction of two example
  8. applications. ]]></description>
复制代码
注意这里使用了 CDATA XML 技术。如果 XML 提要中包含了可以与所含的 XML 标记进行交互的 mark-up 元素,那么则需要使用 CDATA。CDATA 的一个副作用是数据是按原样获取的,因此您需要对文本进行额外的格式化,以便正确显示到目标用户界面中。本教程后面演示的示例应用程序中将看到这样一个例子。最后,如果用户对这一主题非常感兴趣的话,可以通过提供的链接找到关于这一特定主题的更多信息:
  1. <link>
  2. <![CDATA[http://www.ibm.com/developerworks/edu/os-dw-os-eclipse-android.html?ca=drs-]]>
  3. </link>
复制代码
本教程没有细究对类别的任何特殊处理,这是因为 RSS 提要中的所有类别都是相同的。显示发布日期是一项最佳实践技术。简要介绍了 RSS 及其用途之后,我们来从较高的层次了解一下 RSS 阅读器应用程序的架构。

Android RSS 阅读器应用程序架构一份简短扼要的教程很难详尽地描述如何构建一个功能丰富的 RSS 阅读器,因此,我们来看一下 RSS 阅读器需要实现哪些功能。然后逐步完善重要的组件以实现 XML 处理并呈现到 Android 平台。完成这些任务后,您将拥有一个具有基本功能的 RSS 阅读器,并且具有一些功能钩子,可以在将来实现进一步扩展。

RSS 阅读器应用程序的主要需求以下各小节将详细介绍 Android RSS 阅读器是如何解决 RSS 阅读器应用程序的这些主要需求的。  指定感兴趣的 RSS 提要不计其数的 Internet 站点都提供 RSS 提要。应用程序需要指定要处理哪些 RSS 提要。一个功能丰富的 RSS 提要可以使用一种或多种方法选择所需的 RSS 提,包括能够从大量站点和通道中进行选择,或者允许用户在一个 EditView 中手动输入感兴趣的提要。为了尽量减少本教程中与非 XML 内容相关的代码,我们对 RSS 提要 URL 进行了硬编码。将菜单实现为一个钩子,以便根据需要添加 RSS 提要选择。获取感兴趣的 RSS 提要在对 RSS 提要进行任何解析和数据处理之前,首先需要从 Internet 中获得它。这意味着需要通过一个 Internet 连接(网络或 WiFi)连接到托管 RSS 提要的站点,然后执行一个 HTTP GET 操作检索 RSS 数据。返回的数据并不是文件形式,而是一个 XML 数据流。将使用 URL 类取回数据。解析 XML 数据流可以使用多种机制解析 XML 数据。这些机制都需要导航数据流并逐一描述各个数据元素,然后保存这些数据。Android SDK 提供了各种不同的 XML 解析器(Parser) ,但是您也可以选择自行创建。Android SDK 还提供了两种最流行的方法,即 DOM 解析器和 SAX 解析器。DOM 方法非常适合复杂的 XML 文档,因为它会在内存中构建面向节点的 XML 数据表示。SAX 方法在遇到新的标记时将使用回调,因而允许应用程序只保存感兴趣的数据。由于 RSS XML 结构的简单性,因此本教程将使用 SAX 解析器。本教程使用了一个 RSSHandler 类实现 SAX 解析器回调函数。保存 RSS 数据从 XML 数据流提取得到的 RSS 提要必须使用一种有用的格式保存。本教程使用了两个 helper 类:RSSFeed 和 RSSItem,它们将从 XML 数据流解析得到的数据保存到内存中。当 XML 数据流解析全部完成后,应用程序将与这些类进行交互以呈现信息。呈现 RSS 提要本教程的示例应用程序使用两个 Activity 类提供用户界面。主屏幕列出了 RSS 提要标题和发布日期,其后是一组 RSS 提要项。当通过一个 tap 选择某个提要项或将其输入 Android Emulator 后,ShowDescription 活动将显示有关该提要项的完整信息,包括它的标题(Title)、发布日期(Publication Date)、描述(Description)和链接(Link)元素。将设置一个用户界面,因此,文本中包含的任何链接如电子邮件或 Web 都处于激活状态 — 如果选择它们,将会发生相应的操作。例如,如果选择链接文本,将启动 Android 浏览器链接到该目标。通过这种方式,RSS 阅读器可以用非常直观的方式访问所有三种级别的信息。这就是 RSS 的强大之处,更具体地说,这就是 RSS 在移动平台中的功能。数据刷新和离线查看定期刷新数据是 RSS 阅读器的一个重要方面,另一个重要功能就是能够在离线时访问信息,例如当您在飞机上,您的 Android 设备处于飞行模式时。本教程的示例应用程序实现了一些这方面的功能,比如一个用于刷新数据的菜单钩子,但是本教程没有讨论数据持久性和调度功能。接下来将构建一个应用程序,获取一个 RSS 提要并在 Android 应用程序中显示。构建应用程序本教程后面的内容将帮助您构建一个 Android RSS 阅读器,主要分两部分讲述。第一部分介绍如何处理 XML 数据流,第二部分站点介绍如何在 Android 用户界面中呈现 RSS 数据。使用 SAX 获取并解析 XML 数据构建 RSS 阅读器应用程序的核心操作是获取并处理 XML 数据。我们将通过一个 Internet 连接和一个 HTTP GET 操作获取 XML 数据。可以使用 Java 中的一些不同的类实现这种 HTTP 处理。本教程演示了如何使用一个 URL 类的实例取回数据。如前面有关应用程序架构的小节所述,本教程将使用 SAX 解析器。如果数据格式相对简单,或者您的数据需求允许选择性地使用数据结构,那么使用 SAX 解析器仅需要最少量的内存和 excel。在深入研究 SAX 解析器的具体应用之前,请首先快速查看一下 RSSFeed 和 RSSItem 类,因为本教程后面的内容将大量提到这两个类。



RSSFeedRSSFeed 类从一个较高级别的角度表示 RSS 提要。RSSFeed 类包含 RSS 数据源的通道部分中感兴趣的元素,还包含一组RSSItem。RSSItem 表示 RSS 通道中的单个项。观察一下这两个重要的类。RSSFeed.java 如清单 4 所示。
清单 4. RSSFeed.java
  1. package com.msi.androidrss;


  2. import java.util.List;
  3. import java.util.Vector;
  4. import com.msi.androidrss.RSSItem;

  5. public class RSSFeed
  6. {
  7. private String _title = null;
  8. private String _pubdate = null;
  9. private int _itemcount = 0;
  10. private List<RSSItem> _itemlist;


  11. RSSFeed()
  12. {
  13. _itemlist = new Vector(0);
  14. }
  15. int addItem(RSSItem item)
  16. {
  17. _itemlist.add(item);
  18. _itemcount++;
  19. return _itemcount;
  20. }
  21. RSSItem getItem(int location)
  22. {
  23. return _itemlist.get(location);
  24. }
  25. List getAllItems()
  26. {
  27. return _itemlist;
  28. }
  29. int getItemCount()
  30. {
  31. return _itemcount;
  32. }
  33. void setTitle(String title)
  34. {
  35. _title = title;
  36. }
  37. void setPubDate(String pubdate)
  38. {
  39. _pubdate = pubdate;
  40. }
  41. String getTitle()
  42. {
  43. return _title;
  44. }
  45. String getPubDate()
  46. {
  47. return _pubdate;
  48. }
  49. }
复制代码

当 SAX 解析引擎在 RSS 源中完成了 XML 数据解析后,将创建一个 RSSFeed 类的实例,现在它包含了在您的应用程序中处理 RSS 数据所需的所有内容。RSSFeed 类包含三个重要元素,以及用于数据处理的 Set-ers 和 Get-ers。这些感兴趣的数据元素包括:
  • Title (_title):使用一个 Java.lang.String 保存通道的标题以便进行显示。
  • Publication Date (_pubdate):使用一个 java.lang.String 保存发布日期以便进行显示。注意,在更复杂的 RSS 阅读器中,您可以使用发布日期信息执行智能更新或刷新操作。
  • List of Items (_itemlist):一个参数化的 java.util.List,用于保存 RSSItem 集合。




RSSItem让我们看一看 RSSFeed 中包含的项列表。该列表中的每个元素都属于 RSSItem 类型。RSSItem 类定义了一些 java.lang.String 类型来保存解析器获得的值。将每个元素放入一个方便的检索类中,这使应用程序逻辑和用户界面的呈现变成了一个非常简单的任务,您稍后就会体会到。RSSItem.java 如清单 5 所示。
清单 5. RSSItem.java
  1. package com.msi.androidrss;

  2. public class RSSItem
  3. {
  4. private String _title = null;
  5. private String _description = null;
  6. private String _link = null;
  7. private String _category = null;
  8. private String _pubdate = null;


  9. RSSItem()
  10. {
  11. }
  12. void setTitle(String title)
  13. {
  14. _title = title;
  15. }
  16. void setDescription(String description)
  17. {
  18. _description = description;
  19. }
  20. void setLink(String link)
  21. {
  22. _link = link;
  23. }
  24. void setCategory(String category)
  25. {
  26. _category = category;
  27. }
  28. void setPubDate(String pubdate)
  29. {
  30. _pubdate = pubdate;
  31. }
  32. String getTitle()
  33. {
  34. return _title;
  35. }
  36. String getDescription()
  37. {
  38. return _description;
  39. }
  40. String getLink()
  41. {
  42. return _link;
  43. }
  44. String getCategory()
  45. {
  46. return _category;
  47. }
  48. String getPubDate()
  49. {
  50. return _pubdate;
  51. }
  52. public String toString()
  53. {
  54. // limit how much text you display
  55. if (_title.length() > 42)
  56. {
  57. return _title.substring(0, 42) + "...";
  58. }
  59. return _title;
  60. }
  61. }
复制代码
RSSItem 类的实例包含与 RSS 提要中单个项有关的文本数据元素。除了用于保存数据的 java.lang.String 字段外,该类还为每个字段包含了一个 Get-er 和 Set-er。最后,该类覆盖了 toString() 方法,因为当 Android 用户界面元素显示项列表时将调用这个方法。请牢记关于 toString() 方法的注释,因为在讨论在 Android 用户界面呈现 RSS 数据时将使用到。





SAX 方法如上文所述,使用 SAX 方法解析 XML 依赖一种回调结构,在这种结构中,解析器将扫描数据流以查找标记,并在处理程序(由您提供)中调用方法来处理数据。该处理程序扩展了一个称为 DefaultHandler 的 Java 类。处理程序中有五种相关的方法。在 XML 解析处理中,每种方法都执行一个单独的、截然不同的重要角色。这五种方法分别是:
  • startDocument:当开始解析文档时调用。它将初始化所需的数据结构。
  • endDocument:在结束文档解析时调用。
  • startElement:当扫描器发现一个新标记时调用。处理程序通常使用这个方法确定文档中的位置,以便为保存数据做好准备。此外,与该元素相关的任何属性都可用于处理、保存和解释。
  • endElement:当扫描器遇到一个结束标记时调用。处理程序通常使用这种方法判断文档位置并保存中间过程产生的数据。在 RSS 示例应用程序中,当遇到 </item> 标记时,每个 RSSItem 被保存到 RSSFeed 对象。
  • characters:当标记中的数据变为可用时调用。例如,在解析 XML 数据时,标题、描述、链接、类别和发布日期元素的内容都被保存在这个方法中。



RSSHandlerRSSHandler 类是运行本教程应用程序的关键。它负责识别并保存 XML 数据流中的有关信息。当 SAX 解析器调用时,将依次调用它的每个方法。RSSHandler 实现一些非常基本的状态处理,从而在 SAX 解析器处理 XML 数据流时正确地识别和保存数据。SAX 方法可以使用多种正确的方式管理状态。虽然与本教程介绍的 RSS 提要解析主题没有必然联系,但是,如果您觉得在解析某一特定 XML 数据源时,使用 SAX 管理状态非常复杂,则可以考虑使用 DOM 解析器。DOM 解析器的工作前提是文档中的所有元素都非常重要,并且您的应用程序需要检测并提取 XML 数据源中的各种元素。SAX 非常适合 RSS 提要,因为 RSS 的关系非常简单。清单 6 展示了 RSSHandler.java 的实现。请迅速查看一下这些方法,以熟悉处理程序的结构。
清单 6. RSSHandler.java
  1. package com.msi.androidrss;

  2. import org.xml.sax.helpers.DefaultHandler;
  3. import org.xml.sax.*;
  4. import android.util.Log;


  5. public class RSSHandler extends DefaultHandler
  6. {

  7. RSSFeed _feed;
  8. RSSItem _item;
  9. String _lastElementName = "";
  10. boolean bFoundChannel = false;
  11. final int RSS_TITLE = 1;
  12. final int RSS_LINK = 2;
  13. final int RSS_DESCRIPTION = 3;
  14. final int RSS_CATEGORY = 4;
  15. final int RSS_PUBDATE = 5;

  16. int depth = 0;
  17. int currentstate = 0;
  18. /*
  19. * Constructor
  20. */
  21. RSSHandler()
  22. {
  23. }

  24. /*
  25. * getFeed - this returns our feed when all of the parsing is complete
  26. */
  27. RSSFeed getFeed()
  28. {
  29. return _feed;
  30. }


  31. public void startDocument() throws SAXException
  32. {
  33. // initialize our RSSFeed object - this will hold our parsed contents
  34. _feed = new RSSFeed();
  35. // initialize the RSSItem object - you will use this as a crutch to grab
  36. // the info from the channel
  37. // because the channel and items have very similar entries..
  38. _item = new RSSItem();

  39. }
  40. public void endDocument() throws SAXException
  41. {
  42. }
  43. public void startElement(String namespaceURI, String localName,String qName,
  44. Attributes atts) throws SAXException
  45. {
  46. depth++;
  47. if (localName.equals("channel"))
  48. {
  49. currentstate = 0;
  50. return;
  51. }
  52. if (localName.equals("image"))
  53. {
  54. // record our feed data - you temporarily stored it in the item :)
  55. _feed.setTitle(_item.getTitle());
  56. _feed.setPubDate(_item.getPubDate());
  57. }
  58. if (localName.equals("item"))
  59. {
  60. // create a new item
  61. _item = new RSSItem();
  62. return;
  63. }
  64. if (localName.equals("title"))
  65. {
  66. currentstate = RSS_TITLE;
  67. return;
  68. }
  69. if (localName.equals("description"))
  70. {
  71. currentstate = RSS_DESCRIPTION;
  72. return;
  73. }
  74. if (localName.equals("link"))
  75. {
  76. currentstate = RSS_LINK;
  77. return;
  78. }
  79. if (localName.equals("category"))
  80. {
  81. currentstate = RSS_CATEGORY;
  82. return;
  83. }
  84. if (localName.equals("pubDate"))
  85. {
  86. currentstate = RSS_PUBDATE;
  87. return;
  88. }
  89. // if you don't explicitly handle the element, make sure you don't wind
  90. // up erroneously storing a newline or other bogus data into one of our
  91. // existing elements
  92. currentstate = 0;
  93. }

  94. public void endElement(String namespaceURI, String localName, String qName)
  95. throws SAXException
  96. {
  97. depth--;
  98. if (localName.equals("item"))
  99. {
  100. // add our item to the list!
  101. _feed.addItem(_item);
  102. return;
  103. }
  104. }

  105. public void characters(char ch[], int start, int length)
  106. {
  107. String theString = new String(ch,start,length);
  108. Log.i("RSSReader","characters[" + theString + "]");

  109. switch (currentstate)
  110. {
  111. case RSS_TITLE:
  112. _item.setTitle(theString);
  113. currentstate = 0;
  114. break;
  115. case RSS_LINK:
  116. _item.setLink(theString);
  117. currentstate = 0;
  118. break;
  119. case RSS_DESCRIPTION:
  120. _item.setDescription(theString);
  121. currentstate = 0;
  122. break;
  123. case RSS_CATEGORY:
  124. _item.setCategory(theString);
  125. currentstate = 0;
  126. break;
  127. case RSS_PUBDATE:
  128. _item.setPubDate(theString);
  129. currentstate = 0;
  130. break;
  131. default:
  132. return;
  133. }

  134. }
  135. }
复制代码



RSSHandler 解释接下来进一步查看一下 RSSHandler 类。注意,该类具有一个 RSSFeed 类的实例。RSSHandler 类的作用是从 SAX 解析器实现回调,并在此过程中形成 RSS 数据的表示以供应用程序使用。startElement 方法指定找到了哪些数据元素,而 characters 方法通过相应的 set 方法向其中一个 RSSItem 成员执行分配。endElement 检查 item 元素的末尾,如果发现数据元素,则将当前的 RSSItem 添加到 RSSFeed。RSSHandler 被设计为自包含形式,以进行 SAX 解析。它的方法都可以响应解析器的事件,构建 RSSFeed,该类随后通过 getFeed 方法使完全填充的 RSSFeed 对象变为可用。设置 SAX现在您大致了解了在进行 SAX 解析时发生的操作,现在查看一下 SAX 解析器的调用。相关的代码位于 RSSFeed 类的 getFeed() 方法中,如清单 7 所示。
清单 7. RSSFeed.java 中的 getFeed() 方法
  1. private RSSFeed getFeed(String urlToRssFeed)
  2. {
  3. try
  4. {
  5. // setup the url
  6. URL url = new URL(urlToRssFeed);

  7. // create the factory
  8. SAXParserFactory factory = SAXParserFactory.newInstance();
  9. // create a parser
  10. SAXParser parser = factory.newSAXParser();

  11. // create the reader (scanner)
  12. XMLReader xmlreader = parser.getXMLReader();
  13. // instantiate our handler
  14. RSSHandler theRssHandler = new RSSHandler();
  15. // assign our handler
  16. xmlreader.setContentHandler(theRssHandler);
  17. // get our data through the url class
  18. InputSource is = new InputSource(url.openStream());
  19. // perform the synchronous parse
  20. xmlreader.parse(is);
  21. // get the results - should be a fully populated RSSFeed instance,
  22. // or null on error
  23. return theRssHandler.getFeed();
  24. }
  25. catch (Exception ee)
  26. {
  27. // if you have a problem, simply return null
  28. return null;
  29. }
  30. }
复制代码

查看 清单 7 中的代码,可以看到,对 SAX 解析器和您的 RSSHandler 类必需的类进行了实例化。当将 RSSHandler 分配给 XMLReader 实例后,就可以开始进行解析。注意,SAX 中最困难的工作就是定义您的处理程序!但是在可以解析数据之前,您必须对数据进行检索。

获取 XML 数据获取 XML 数据流的 HTTP 事务通过 URL 类实现,将它的 Stream 传递给 InputSource 类的新实例。SAX 解析器/扫描器在分配的处理程序内调用各种方法,使用 InputSource 导航数据流并执行解析操作。在本例中,这些方法都位于 RSSHandler 类中。一旦解析完成,将从 RSSHandler 中检索 RSSFeed,RSSHandler 在每次回调操作期间构建一个 RSSFeed 类的实例。try/catch 块中将尝试一个解析操作,如果操作成功,将返回一个 RSSFeed 的实例。如果发生错误,将抛出异常并且函数返回 null。当一个完整的 RSSFeed 类的实例可用后,将开始向用户呈现数据.
在 Android 中呈现 RSS 数据现在,RSS 提要中的 XML 数据安全地保存在内存中 RSSFeed 的实例中,该实例使用一个方便的 List 结构包含了许多 RSSItem。RSS 阅读器的目的就是管理数据并以一种整洁的方式呈现给用户。在这里,Android 用户界面代码和各种资源将进行交互。本节将介绍 Android 用户界面的实现以及显示 RSS 数据的方式。主用户界面RSSReader 应用程序的启动 Activity 是 RSSReader 类。Activity 的入口点是 onCreate 方法。该方法负责启动用户界面,在某些情形下,例如在本例中,除创建用户界面外,它还将发起后续操作。查看 RSSReader.java 中的 onCreate 方法,如清单 8 所示,可以看到应用程序的结构非常简单。
清单 8. The RSSReader 的 onCreate 方法
  1. private RSSFeed feed = null;

  2. public void onCreate(Bundle icicle) {
  3. super.onCreate(icicle);
  4. setContentView(R.layout.main);

  5. // go get our feed!
  6. feed = getFeed(RSSFEEDOFCHOICE);

  7. // display UI
  8. UpdateDisplay();

  9. }
复制代码

onCreate 方法执行以下三个功能:
  • 创建由 R.layout.main 确定的用户界面并呈现 main.xml 中包含的布局。
  • 通过调用 getFeed() 方法创建一个 RSSFeed 类的实例。
  • 通过 UpdateDisplay() 方法更新用户界面以反映 RSS 提要内容。



用户界面布局RSSReader Activity 的用户界面包括两个 TextView 和一个 ListView。TextView 显示通道标题和发布日期,而 ListView 显示 RSSFeed 中的 RSSItems 列表。清单 9 包含了 Activity 主用户界面的布局。
清单 9. main.xml 包含 RSSReader Activity 的用户界面定义
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:orientation="vertical"
  4. android:layout_width="fill_parent"
  5. android:layout_height="fill_parent"
  6. >
  7. <TextView
  8. android:layout_width="fill_parent"
  9. android:layout_height="wrap_content"
  10. android:text="Android RSSReader"
  11. android:id="@+id/feedtitle"
  12. />
  13. <TextView
  14. android:layout_width="fill_parent"
  15. android:layout_height="wrap_content"
  16. android:text=""
  17. android:id="@+id/feedpubdate"
  18. />
  19. <ListView
  20. android:layout_width="fill_parent"
  21. android:layout_height="fill_parent"
  22. android:id="@+id/itemlist"

  23. />
  24. </LinearLayout>
复制代码

main.xml 中的布局具有非常简单的用户界面视图。注意每个视图中出现的 android:id 属性。这样做是有其意义的,因为应用程序将动态更新每个视图的内容。





呈现查看清单 10 中的 UpdateDisplay 方法,了解 RSSFeed 数据如何连接到用户界面。RSSFeed 类的实例现在包含 RSS 数据。执行UpdateDisplay 方法,获取 RSS 数据并通过 Android 用户界面呈现。
清单 10. UpdateDisplay 方法将数据连接到用户界面
  1. private void UpdateDisplay()
  2. {
  3. TextView feedtitle = (TextView) findViewById(R.id.feedtitle);
  4. TextView feedpubdate = (TextView) findViewById(R.id.feedpubdate);
  5. ListView itemlist = (ListView) findViewById(R.id.itemlist);


  6. if (feed == null)
  7. {
  8. feedtitle.setText("No RSS Feed Available");
  9. return;
  10. }

  11. feedtitle.setText(feed.getTitle());
  12. feedpubdate.setText(feed.getPubDate());


  13. ArrayAdapter<RSSItem> adapter = new
  14. ArrayAdapter<RSSItem>(this,android.R.layout.
  15. simple_list_item_1,feed.getAllItems());

  16. itemlist.setAdapter(adapter);

  17. itemlist.setSelection(0);

  18. itemlist.setOnItemClickListener(this);


  19. }
复制代码

查看清单 10,UpdateDisplay 方法通过 findViewById() 方法将两个 TextView 对象和一个 ListView 对象连接到布局,并分别传入标识符。如果 RSSFeed 为 null,该方法将显示一条消息表示 RSS 提要可用。通道标题和发布日期分别使用 TextView 对象显示。
RSSItems 列表要将 RSSItems 列表连接到 ListView,需要创建一个 ArrayAdapter 类的实例。构建这个参数化的类是为了管理 RSSItem 类型的项。列表布局由内置的资源 simple_list_item_1 管理,该内置资源实质上是一个列表,其中每个条目支持一行文本。还需通过 RSSFeed 类的getAllItems() 方法传递 RSSFeed 中的所有 RSSItem 条目的列表。上文提到过,RSSItem 类覆盖了默认的 toString() 方法,允许ArrayAdapter 获得有意义的表示以便在 ListView 中显示。清单 11 显示这个方法如何实现相同的格式化以确保文本恰当地嵌入到 ListView 中。
清单 11. 覆盖 RSSItem 类的默认的 toString() 方法
  1. public String toString()
  2. {
  3. // limit how much text you display
  4. if (_title.length() > 42)
  5. {
  6. return _title.substring(0, 42) + "...";
  7. }
  8. return _title;
  9. }
复制代码

ArrayAdapter 被分配给 ListView,并且通过使用参数 0 调用 setSelection 选择了第一个条目。最后,您需要使用 setOnItemClickListener 方法创建一个侦听器来响应项选择事件。RSSReader 类实现 OnItemClickListener 界面:public class RSSReader extends Activity implements OnItemClickListener。侦听器功能由 onItemClick 方法实现,如清单 12 所示。
清单 12. onItemClick 实现
  1. public void onItemClick(AdapterView parent, View v, int position, long id)
  2. {
  3. Log.i(tag,"item clicked! [" + feed.getItem(position).getTitle() + "]");

  4. Intent itemintent = new Intent(this,ShowDescription.class);

  5. Bundle b = new Bundle();
  6. b.putString("title", feed.getItem(position).getTitle());
  7. b.putString("description", feed.getItem(position).getDescription());
  8. b.putString("link", feed.getItem(position).getLink());
  9. b.putString("pubdate", feed.getItem(position).getPubDate());

  10. itemintent.putExtra("android.intent.extra.INTENT", b);

  11. startSubActivity(itemintent,0);
  12. }
复制代码

当选择 ListView 的某个项后,应用程序将显示所选的 RSSItem 的 Description 元素。查看 onItemClick 中的代码并注意以下三点:
  • 需要创建 Intent 来启动另一个 Activity,ShowDescription Activity 在 ShowDescription.java 中定义
  • 使用 Bundle 将数据传递给被调用的行为
  • 以子行为(sub activity)的方式启动 ShowDescription 行为,它可帮助应用程序实现同步功能
ShowDescriptionShowDescription Activity 提供了所选 RSSItem 的下一层细节,如图 2 所示。
图 2. 所选 RSSItem 的下一层细节

清单 13 列出了 ShowDescription 类。注意解除绑定 RSSItem 数据以及格式化用于用户界面的字符串的过程。
清单 13. ShowDescription 行为
  1. package com.msi.androidrss;

  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.widget.Button;
  5. import android.widget.TextView;
  6. import android.content.Intent;
  7. import android.view.*;

  8. public class ShowDescription extends Activity
  9. {
  10. public void onCreate(Bundle icicle)
  11. {
  12. super.onCreate(icicle);
  13. setContentView(R.layout.showdescription);

  14. String theStory = null;


  15. Intent startingIntent = getIntent();

  16. if (startingIntent != null)
  17. {
  18. Bundle b = startingIntent.getBundleExtra("android.intent.extra.INTENT");
  19. if (b == null)
  20. {
  21. theStory = "bad bundle?";
  22. }
  23. else
  24. {
  25. theStory = b.getString("title") + "\n\n" + b.getString("pubdate")
  26. + "\n\n" + b.getString("description").replace('\n',' ')
  27. + "\n\nMore information:\n" + b.getString("link");
  28. }
  29. }
  30. else
  31. {
  32. theStory = "Information Not Found.";

  33. }

  34. TextView db= (TextView) findViewById(R.id.storybox);
  35. db.setText(theStory);

  36. Button backbutton = (Button) findViewById(R.id.back);

  37. backbutton.setOnClickListener(new Button.OnClickListener()
  38. {
  39. public void onClick(View v)
  40. {
  41. finish();
  42. }
  43. });
  44. }
  45. }
复制代码

需要确保 Activity 没有尝试处理一个 null Bundle 或显示错误的数据。确保变量 theStory 始终具有一个有效的值。





back 按钮代码使用一个简单的 Button OnClickListener 终止这个 Activity。由于这个 Activity 使用 startSubActivity() 方法启动,因此调用finish() 将导致控制权返回给调用的 Activity,即 RSSReader。注意,描述的文本表示包含一个超链接。Android 通过属性 android:autoLink="all" 自动处理这一点,如清单 14 所示。
清单 14. ShowDescription.xml 定义 ShowDescription Activity 的用户界面
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:orientation="vertical"
  4. android:layout_width="fill_parent"
  5. android:layout_height="fill_parent"
  6. >
  7. <TextView
  8. android:layout_width="fill_parent"
  9. android:layout_height="wrap_content"
  10. android:autoLink="all"
  11. android:text="story goes here ...."
  12. android:id="@+id/storybox"
  13. />
  14. <Button
  15. android:layout_width="wrap_content"
  16. android:layout_height="wrap_content"
  17. android:text="Back"
  18. android:id="@+id/back"
  19. />

  20. </LinearLayout>
复制代码

通过链接文本,使用 RSSItem 链接启动 Web 站点是一个零代码操作,如图 3 和图 4 所示。
图 3. 文本中的 Web 站点引用将自动使用超链接

图 4 展示了从 RSSItem 链接元素启动的 Web 页面。它演示了 RSS 的强大和高效性。在选择提要项时,将显示感兴趣的主题。您将看到一些简洁的摘要,如果这些内容提供了足够的信息量,那么就可以满足您的需求。如果您希望查看更多的内容,那么可以单击链接,从而获得更详细的信息。
图 4. 启动 RSSItem 链接元素中的 Web 页面

大功告成!您现在拥有了一个可以工作的 RSS 阅读器!

扩展钩子在源代码中,最后要查看的是用于设置菜单的 RSSReader.java 文件。本教程没有解决选择和刷新 RSS 提要的问题,但是,您将看到提供了一些钩子,用于在未来的教程中实现这些功能。清单 15 包含了两个实现菜单行为的方法。
清单 15. 菜单处理方法
  1. public boolean onCreateOptionsMenu(Menu menu)
  2. {
  3. super.onCreateOptionsMenu(menu);

  4. menu.add(0,0,"Choose RSS Feed");
  5. menu.add(0,1,"Refresh");
  6. Log.i(tag,"onCreateOptionsMenu");
  7. return true;
  8. }

  9. public boolean onOptionsItemSelected(Menu.Item item){
  10. switch (item.getId()) {
  11. case 0:

  12. Log.i(tag,"Set RSS Feed");
  13. return true;
  14. case 1:
  15. Log.i(tag,"Refreshing RSS Feed");
  16. return true;
  17. }
  18. return false;
  19. }
复制代码

在 Activity 生命周期中将调用一次 onCreateOptionsMenu(),它允许创建菜单项。该方法的第二个参数是菜单的惟一标识符。当用户选择某个项时将调用 onOptionsItemSelected 方法。通过 getId() 方法使用菜单项的标识符,可以简单地响应特定的菜单选择。对功能进行扩展超出了本教程的讨论范围;这些方法可以作为增强 Android RSS 阅读器的起点!
结束语结束语本教程演示了如何为 Android 构建一个 RSS 阅读器应用程序。通过对 RSS 数据提要结构的讨论,我们发现 SAX 解析方法非常适合 RSS version 2.0 的简单 XML 结构。使用 SAX 处理 XML 数据时,由回调处理程序使用的五种方法保证了解析 RSS 数据的效率。在解析和组织 RSS 数据时,可通过一个定制的视图呈现数据。对于感兴趣的读者,本教程还提供了一些钩子,以便将这个示例应用程序进一步扩展为一个功能更丰富的 RSS 阅读器。
下载
描述
名字
大小
下载方法
教程源代码
60KB









本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

点评

已经有了。。。  发表于 2012-10-29 22:17

0

主题

42

帖子

33

安币

程序猿

Rank: 2

发表于 2011-11-4 15:12:19 | 显示全部楼层
GERGHREGERGgsgfdsg 的施工方的

主题

帖子

安币

游客

发表于 2011-11-15 16:11:53 | 显示全部楼层
学习下~~~

0

主题

9

帖子

46

安币

初级码农

Rank: 1

发表于 2011-11-29 13:58:22 | 显示全部楼层
不错哦 很详细谢谢了

0

主题

9

帖子

3

安币

初级码农

Rank: 1

发表于 2011-12-23 16:21:57 | 显示全部楼层
xie xie !! thanks for sharing

0

主题

29

帖子

22

安币

初级码农

Rank: 1

发表于 2011-12-23 17:07:28 | 显示全部楼层
非常感谢LZ的分享, 我是来学习的..............

0

主题

35

帖子

22

安币

初级码农

Rank: 1

发表于 2011-12-29 12:46:12 | 显示全部楼层
学习,,,,,,,,,,,,,,,

0

主题

20

帖子

40

安币

初级码农

Rank: 1

发表于 2012-1-8 07:45:12 | 显示全部楼层
好好学习,谢谢分享。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

站长推荐

通过邮件订阅最新安卓weekly信息
上一条 /4 下一条

下载安卓巴士客户端

全国最大的安卓开发者社区
联系我们
关闭
合作电话:
15618560077
Email:
805941275@qq.com
商务市场合作/投稿
问题反馈及帮助
联系我们

广告投放| 下载客户端|申请友链|手机版|站点统计|安卓巴士 ( 粤ICP备15117877号 )

快速回复 返回顶部 返回列表