Android使用XML全攻略
入門
在本文中,您將學(xué)習(xí)如何構(gòu)建通過 Internet 使用 XML 的 Android 應(yīng)用程序。Android 應(yīng)用程序是使用 Java™ 編程語言編寫的,因此具備 Java 技術(shù)方面的經(jīng)驗(yàn)是必需的。要進(jìn)行 Android 開發(fā),您需要使用 android sdk。 本文中的所有代碼適用于任何版本的 Android SDK,但 SDK 1.5_pre 是用于開發(fā)代碼的。您可以使用 SDK 和一個(gè)文本編輯器來開發(fā) Android 應(yīng)用程序,但使用Android Developer Tools (ADT)(一款 Eclipse 插件)會(huì)更加簡單。在本文中,我們使用 0.9 版本的 ADT 和 Eclipse 3.4.2, Java 版本。有關(guān)所有這些工具的鏈接,請(qǐng)參見 參考資料。
Android上的XML
Android 平臺(tái)是一個(gè)開源移動(dòng)開發(fā)平臺(tái)。它允許您訪問各種移動(dòng)設(shè)備的所有方面,這些移動(dòng)設(shè)備從低級(jí)圖形設(shè)備到手機(jī)攝像頭上的硬件不一而足。由于 Android 可以實(shí)現(xiàn)這么豐富的功能,因此您可能想知道為何還要為 XML 傷腦筋呢。并不是因?yàn)槭褂?XML 是多么地有趣;而是因?yàn)樗芴峁┮恍┨厥獾闹С帧ML經(jīng)常用作 Internet上的一種數(shù)據(jù)格式。如果您希望通過 Internet 訪問數(shù)據(jù),則數(shù)據(jù)很有可能是 XML 格式。如果您希望發(fā)送數(shù)據(jù)給 Web 服務(wù),那么您可能也需要發(fā)送 XML。簡而言之,如果您的Android應(yīng)用程序?qū)⒗?Internet,那么您可能需要使用 XML。幸運(yùn)的是,您可以采用多種方法在Android上使用 XML。
XML 解析器常用縮略語
- API:應(yīng)用程序編程接口(Application programming interface)
- RSS:Really Simple Syndication
- SDK:軟件開發(fā)包(Software Developers Kit)
- UI:用戶界面(User interface)
- URL:通用資源定位符(Universal Resource Locator)
- XML:可擴(kuò)展標(biāo)記語言(Extensible Markup Language)
Android 平臺(tái)最大的一個(gè)優(yōu)勢(shì)在于它利用了 Java 編程語言。Android SDK 并未向您的標(biāo)準(zhǔn) Java Runtime Environment (JRE) 提供一切可用功能,但它支持其中很大一部分功能。Java 平臺(tái)支持通過許多不同的方式來使用 XML,并且大多數(shù)與 XML 相關(guān)的 Java API 在 Android 上得到了完全支持。舉例來說,Java 的 Simple API for XML (SAX) 和 Document Object Model (DOM) 在 Android 上都是可用的。這些 API 多年以來一直都是 Java 技術(shù)的一部分。較新的 Streaming API for XML (StAX) 在 Android 中并不可用。但是, Android 提供了一個(gè)功能相當(dāng)?shù)膸?。最后,Java XML Binding API 在 Android 中也不可用。這個(gè) API 已確定可以在 Android 中實(shí)現(xiàn)。但是,它更傾向于是一個(gè)重量級(jí)的 API,需要使用許多不同類的實(shí)例來表示 XML 文檔。因此,這對(duì)于受限的環(huán)境,比如說 Android 針對(duì)的手持設(shè)備,不太理想。在后續(xù)小節(jié)中,我們將以 Internet 上的一個(gè)簡單的 XML 源為例,來看看如何在 Android 應(yīng)用程序中使用上述各種 API 來解析它。首先,我們來看看這個(gè)簡單應(yīng)用程序的主要部分,它將通過 Internet 來使用 XML。
Android 新聞閱讀器
應(yīng)用程序?qū)臒衢T Android 開發(fā)人員站點(diǎn) Androidster 獲取一個(gè) RSS 提要,并將它解析為一組簡單的 Java 對(duì)象,您可以使用這些對(duì)象構(gòu)建一個(gè) Android ListView(參見下載部分獲取源代碼)。這是一種典型的多態(tài)行為 — 提供相同行為的不同實(shí)現(xiàn)(不同的 XML 解析算法)。清單 1 展示了如何在 Java 代碼中使用一個(gè)接口建立這一模型。
清單 1. XML 提要解析器接口[size=0.76em]
- package org.developerworks.android;
- import java.util.List;
- public interface FeedParser {
- List<Message> parse();
- }
在清單2中,Message 類是一個(gè)典型的 Plain Old Java Object (POJO),它表示一種數(shù)據(jù)結(jié)構(gòu)。
清單 2. Message POJO
- public class Message implements Comparable<Message>{
- static SimpleDateFormat FORMATTER =
- new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z");
- private String title;
- private URL link;
- private String description;
- private Date date;
- // getters and setters omitted for brevity
- public void setLink(String link) {
- try {
- this.link = new URL(link);
- } catch (MalformedURLException e) {
- throw new RuntimeException(e);
- }
- }
- public String getDate() {
- return FORMATTER.format(this.date);
- }
- public void setDate(String date) {
- // pad the date if necessary
- while (!date.endsWith("00")){
- date += "0";
- }
- try {
- this.date = FORMATTER.parse(date.trim());
- } catch (ParseException e) {
- throw new RuntimeException(e);
- }
- }
- @Override
- public String toString() {
- // omitted for brevity
- }
- @Override
- public int hashCode() {
- // omitted for brevity
- }
- @Override
- public boolean equals(Object obj) {
- // omitted for brevity
- }
- // sort by date
- public int compareTo(Message another) {
- if (another == null) return 1;
- // sort descending, most recent first
- return another.date.compareTo(date);
- }
- }
清單 2 中的消息基本上是相當(dāng)直觀的。通過允許日期和鏈接作為簡單的對(duì)象被訪問,同時(shí)將它們表示為較強(qiáng)類型的對(duì)象(java.util.Date 和 java.net.URL),它隱藏了一些內(nèi)部狀態(tài)。它是一個(gè)典型的 Value Object,因此它基于其內(nèi)部狀態(tài)實(shí)現(xiàn)了equals() 和 hashCode()。它還實(shí)現(xiàn)了 Comparable 接口,因此您可以使用它進(jìn)行排序(按日期)。在實(shí)踐中,提要中的數(shù)據(jù)始終是有序的,因?yàn)闆]有必要再進(jìn)行排序。
每個(gè)解析器實(shí)現(xiàn)都需要提供一個(gè)URL給Androidster提要,并使用它打開一個(gè)到Androidster站點(diǎn)的HTTP連接。這一常見行為自然是在 Java 代碼中建模,我們使用了一個(gè)抽象基類,如清單3所示。
清單 3. 基本提要解析器類
- public abstract class BaseFeedParser implements FeedParser {
- // names of the XML tags
- static final String PUB_DATE = "pubDate";
- static final String DESCRIPTION = "description";
- static final String LINK = "link";
- static final String TITLE = "title";
- static final String ITEM = "item";
- final URL feedUrl;
- protected BaseFeedParser(String feedUrl){
- try {
- this.feedUrl = new URL(feedUrl);
- } catch (MalformedURLException e) {
- throw new RuntimeException(e);
- }
- }
- protected InputStream getInputStream() {
- try {
- return feedUrl.openConnection().getInputStream();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- }
基類存儲(chǔ) feedUrl 并使用它打開了一個(gè) java.io.InputStream。如果出現(xiàn)任何差錯(cuò),它會(huì)拋出一個(gè) RuntimeException,造成應(yīng)用程序出現(xiàn)故障?;愡€為標(biāo)記的名稱定義了一些簡單的常量。清單 4 顯示了提要中的一些示例內(nèi)容,以便于您理解這些標(biāo)記的重要性。
清單 4.示例XML提要
- <?xml version="1.0" encoding="UTF-8"?>
- <!-- generator="FeedCreator 1.7.2" -->
- <rss version="2.0">
- <channel>
- <title>android_news</title>
- <description>android_news</description>
- <link><a href="\"http://www.androidster.com/android_news.php</link>\"" target="\"_blank\"">http://www.androidster.com/android_news.php</link></a>
- <lastBuildDate>Sun, 19 Apr 2009 19:43:45 +0100</lastBuildDate>
- <generator>FeedCreator 1.7.2</generator>
- <item>
- <title>Samsung S8000 to Run Android, Play DivX, Take Over the
- World</title>
- <link><a href="\"http://www.androidster.com/android_news/samsung-s8000-to-run-android-\"" target="\"_blank\"">http://www.androidster.com/andro ... 000-to-run-android-</a>
- play-divx-take-over-the-world</link>
- <description>More details have emerged on the first Samsung handset
- to run Android. A yet-to-be announced phone called the S8000 is being
- reported ...</description>
- <pubDate>Thu, 16 Apr 2009 07:18:51 +0100</pubDate>
- </item>
- <item>
- <title>Android Cupcake Update on the Horizon</title>
- <link><a href="\"http://www.androidster.com/android_news/android-cupcake-update-\"" target="\"_blank\"">http://www.androidster.com/android_news/android-cupcake-update-</a>
- on-the-horizon</link>
- <description>After months of discovery and hearsay, the Android
- build that we have all been waiting for is about to finally make it
- out ...</description>
- <pubDate>Tue, 14 Apr 2009 04:13:21 +0100</pubDate>
- </item>
- </channel>
- </rss>
如清單4中的示例所示,一個(gè)ITEM對(duì)應(yīng)于一個(gè)Message實(shí)例。項(xiàng)目的子節(jié)點(diǎn)(TITLE、LINK 等)對(duì)應(yīng)于 Message 實(shí)例的屬性?,F(xiàn)在,您已經(jīng)對(duì)提要有了一定的認(rèn)識(shí),并且已經(jīng)創(chuàng)建了所有常用部分,接下來看看如何使用 Android 上可用的各種技術(shù)來解析這個(gè)提要。您將從 SAX 開始。
使用 SAX
在Java環(huán)境中,當(dāng)您需要一個(gè)速度快的解析器并且希望最大限度減少應(yīng)用程序的內(nèi)存占用時(shí),通??梢允褂?SAX API。這非常適用于運(yùn)行 Android 的移動(dòng)設(shè)備。您可以在 Java 環(huán)境中照原樣使用 SAX API,在 Android 上運(yùn)行它不需要做任何修改。清單 5 顯示了FeedParser 接口的一個(gè) SAX 實(shí)現(xiàn)。
清單 5. SAX 實(shí)現(xiàn)
- public class SaxFeedParser extends BaseFeedParser {
- protected SaxFeedParser(String feedUrl){
- super(feedUrl);
- }
- public List<Message> parse() {
- SAXParserFactory factory = SAXParserFactory.newInstance();
- try {
- SAXParser parser = factory.newSAXParser();
- RssHandler handler = new RssHandler();
- parser.parse(this.getInputStream(), handler);
- return handler.getMessages();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- }
如果您以前使用過 SAX,那么這對(duì)您肯定非常熟悉。與任何 SAX 實(shí)現(xiàn)相同,大多數(shù)細(xì)節(jié)都在 SAX 處理程序中。在分解 XML 文檔時(shí),處理程序從 SAX 解析器接收事件。在本例中,您創(chuàng)建了一個(gè)新的名稱為 RssHandler 的類,并將它注冊(cè)為解析器的處理程序,如 清單 6 所示。
清單 6. SAX 處理程序
- import static org.developerworks.android.BaseFeedParser.*;
- public class RssHandler extends DefaultHandler{
- private List<Message> messages;
- private Message currentMessage;
- private StringBuilder builder;
- public List<Message> getMessages(){
- return this.messages;
- }
- @Override
- public void characters(char[] ch, int start, int length)
- throws SAXException {
- super.characters(ch, start, length);
- builder.append(ch, start, length);
- }
- @Override
- public void endElement(String uri, String localName, String name)
- throws SAXException {
- super.endElement(uri, localName, name);
- if (this.currentMessage != null){
- if (localName.equalsIgnoreCase(TITLE)){
- currentMessage.setTitle(builder.toString());
- } else if (localName.equalsIgnoreCase(LINK)){
- currentMessage.setLink(builder.toString());
- } else if (localName.equalsIgnoreCase(DESCRIPTION)){
- currentMessage.setDescription(builder.toString());
- } else if (localName.equalsIgnoreCase(PUB_DATE)){
- currentMessage.setDate(builder.toString());
- } else if (localName.equalsIgnoreCase(ITEM)){
- messages.add(currentMessage);
- }
- builder.setLength(0);
- }
- }
- @Override
- public void startDocument() throws SAXException {
- super.startDocument();
- messages = new ArrayList<Message>();
- builder = new StringBuilder();
- }
- @Override
- public void startElement(String uri, String localName, String name,
- Attributes attributes) throws SAXException {
- super.startElement(uri, localName, name, attributes);
- if (localName.equalsIgnoreCase(ITEM)){
- this.currentMessage = new Message();
- }
- }
- }
RssHandler 類擴(kuò)展了 org.xml.sax.helpers.DefaultHandler 類。該類為 SAX 解析器生成的事件所對(duì)應(yīng)的所有方法都提供了一個(gè)默認(rèn)的非操作實(shí)現(xiàn)。這允許子類根據(jù)需要僅覆蓋一些方法。RssHandler 提供了一個(gè)額外的 API,即 getMessages。它返回處理程序在從 SAX 解析器接收事件時(shí)所收集的 Message 對(duì)象列表。它有另外兩個(gè)內(nèi)部變量,currentMessage 針對(duì)被解析的 Message 實(shí)例,以及名稱為 builder 的 StringBuilder 變量,用于存儲(chǔ)文本節(jié)點(diǎn)中的字符數(shù)據(jù)。解析器將相應(yīng)事件發(fā)送給處理程序時(shí)會(huì)調(diào)用 startDocument 方法,這兩個(gè)變量的初始化操作就是在此時(shí)完成。
查看清單6中的startElement方法。在XML文檔中每次遇到開始標(biāo)記時(shí)都會(huì)調(diào)用它。您只關(guān)心該標(biāo)記何時(shí)為ITEM標(biāo)記。對(duì)于這種情況,您將創(chuàng)建一個(gè)新的Message。現(xiàn)在來看characters方法。遇到文本節(jié)點(diǎn)中的字符數(shù)據(jù)時(shí)便會(huì)調(diào)用此方法。數(shù)據(jù)只是被添加到builder變量中。最后,我們來看endElement方法。遇到結(jié)束標(biāo)記時(shí)會(huì)調(diào)用此方法。對(duì)于與某Message屬性相對(duì)應(yīng)的標(biāo)記,如TITLE和LINK,則使用builder變量中的數(shù)據(jù)在currentMessage上設(shè)置適當(dāng)?shù)膶傩?。如果結(jié)束標(biāo)記是一個(gè)ITEM,則currentMessage將被添加到Messages列表中。所有這些都是非常典型的SAX解析;此處的一切都不是Android所特有的。因此,如果您知道如何編寫Java SAX解析器,則應(yīng)該知道如何編寫Android SAX解析器。但是,Android SDK確實(shí)在SAX上添加了一些便捷的特性。
別走開,精彩下頁繼續(xù)
#p#
更加簡單的 SAX 解析
Android SDK 提供了一個(gè)名稱為android.util.Xml的實(shí)用類。清單 7 展示了如何使用這個(gè)相同的實(shí)用類來設(shè)置一個(gè) SAX 解析器。
清單 7. Android SAX 解析器
- public class AndroidSaxFeedParser extends BaseFeedParser {
- public AndroidSaxFeedParser(String feedUrl) {
- super(feedUrl);
- }
- public List<Message> parse() {
- RssHandler handler = new RssHandler();
- try {
- Xml.parse(this.getInputStream(), Xml.Encoding.UTF_8, handler);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- return handler.getMessages();
- }
- }
注意,這個(gè)類仍然使用了一個(gè)標(biāo)準(zhǔn)的 SAX 處理程序,因此您僅僅重用了 清單 7 中所示的 RssHandler。能夠重用 SAX 處理程序是非常不錯(cuò)的,但其代碼稍微有些復(fù)雜。您可以想像,如果需要解析一個(gè)更加復(fù)雜的 XML 文檔,則處理程序可能會(huì)帶來各種各樣的 bug。舉例來說,回頭看看 清單 6 中的 endElement 方法。注意,在嘗試設(shè)置屬性之前,它檢查了 currentMessage 是否為 null?,F(xiàn)在,再回頭看看 清單 4 中的示例 XML。 注意,ITEM 標(biāo)記外部有一些 TITLE 和 LINK 標(biāo)記。這就是使用 null 檢查的原因。否則,每一個(gè) TITLE標(biāo)記 會(huì)導(dǎo)致一個(gè) NullPointerException。Android 提供了自己獨(dú)有的 SAX API(參見 清單 8),它排除了您編寫自己的 SAX 處理程序的需要。
清單 8. 經(jīng)過簡化的 Android SAX 解析器
- public class AndroidSaxFeedParser extends BaseFeedParser {
- public AndroidSaxFeedParser(String feedUrl) {
- super(feedUrl);
- }
- public List<Message> parse() {
- final Message currentMessage = new Message();
- RootElement root = new RootElement("rss");
- final List<Message> messages = new ArrayList<Message>();
- Element channel = root.getChild("channel");
- Element item = channel.getChild(ITEM);
- item.setEndElementListener(new EndElementListener(){
- public void end() {
- messages.add(currentMessage.copy());
- }
- });
- item.getChild(TITLE).setEndTextElementListener(new EndTextElementListener(){
- public void end(String body) {
- currentMessage.setTitle(body);
- }
- });
- item.getChild(LINK).setEndTextElementListener(new EndTextElementListener(){
- public void end(String body) {
- currentMessage.setLink(body);
- }
- });
- item.getChild(DESCRIPTION).setEndTextElementListener(new
- EndTextElementListener(){
- public void end(String body) {
- currentMessage.setDescription(body);
- }
- });
- item.getChild(PUB_DATE).setEndTextElementListener(new EndTextElementListener(){
- public void end(String body) {
- currentMessage.setDate(body);
- }
- });
- try {
- Xml.parse(this.getInputStream(), Xml.Encoding.UTF_8,
- root.getContentHandler());
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- return messages;
- }
- }
新的SAX 解析代碼并未使用 SAX 處理程序,而是使用了 SDK 中的 android.sax 包中的類。這些類允許您構(gòu)建 XML 文檔的結(jié)構(gòu),并根據(jù)需要添加事件監(jiān)聽程序。在以上代碼中,您聲明文檔將有一個(gè) rss 根元素,并且它有一個(gè) channel 子元素。然后,您聲明channel 將有一個(gè) ITEM 子元素,并且開始添加監(jiān)聽程序。對(duì)于每個(gè)監(jiān)聽程序,您都使用了一個(gè)實(shí)現(xiàn)了特定接口(EndElementListner或 EndTextElementListener)的匿名內(nèi)部類。注意,您不需要跟蹤字符數(shù)據(jù)。不僅僅因?yàn)檫@樣會(huì)更加簡單,更重要的是更加高效。最后,在調(diào) 用 Xml.parse 實(shí)用方法時(shí),您將傳遞一個(gè)通過根元素生成的處理程序。
清單 8 中的所有代碼都是可選的。如果您習(xí)慣 Java 環(huán)境中的標(biāo)準(zhǔn) SAX 解析代碼,那么您可以堅(jiān)持使用它。如果您希望嘗試 Android SDK 所提供的便捷的包裝器,那么也可以使用它。如果您完全不希望使用 SAX 會(huì)怎樣呢?可以使用一些備選方案。其中的首選方法就是 DOM。
使用 DOM
Android 完全支持 DOM 解析,就像在桌面機(jī)器或服務(wù)器上使用 Java 代碼運(yùn)行它一樣。清單 9 顯示了一個(gè)基于 DOM 的解析器接口實(shí)現(xiàn)。
清單 9. 基于 DOM 的提要解析器實(shí)現(xiàn)
- public class DomFeedParser extends BaseFeedParser {
- protected DomFeedParser(String feedUrl) {
- super(feedUrl);
- }
- public List<Message> parse() {
- DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
- List<Message> messages = new ArrayList<Message>();
- try {
- DocumentBuilder builder = factory.newDocumentBuilder();
- Document dom = builder.parse(this.getInputStream());
- Element root = dom.getDocumentElement();
- NodeList items = root.getElementsByTagName(ITEM);
- for (int i=0;i<items.getLength();i++){
- Message message = new Message();
- Node item = items.item(i);
- NodeList properties = item.getChildNodes();
- for (int j=0;j<properties.getLength();j++){
- Node property = properties.item(j);
- String name = property.getNodeName();
- if (name.equalsIgnoreCase(TITLE)){
- message.setTitle(property.getFirstChild().getNodeValue());
- } else if (name.equalsIgnoreCase(LINK)){
- message.setLink(property.getFirstChild().getNodeValue());
- } else if (name.equalsIgnoreCase(DESCRIPTION)){
- StringBuilder text = new StringBuilder();
- NodeList chars = property.getChildNodes();
- for (int k=0;k<chars.getLength();k++){
- text.append(chars.item(k).getNodeValue());
- }
- message.setDescription(text.toString());
- } else if (name.equalsIgnoreCase(PUB_DATE)){
- message.setDate(property.getFirstChild().getNodeValue());
- }
- }
- messages.add(message);
- }
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- return messages;
- }
- }
與第一個(gè) SAX 示例類似,以上代碼完全沒有特定于 Android 的地方。DOM 解析器將所有 XML 文檔讀取到內(nèi)存中,然后允許您使用 DOM API 遍歷 XML 樹、檢索所需的數(shù)據(jù)。這是非常直觀的代碼,并且,在某些方面比基于 SAX 的實(shí)現(xiàn)更加簡單。但是,DOM 通常更加占用內(nèi)存,因?yàn)橐磺袃?nèi)容都會(huì)先讀取到內(nèi)存中。這對(duì)于運(yùn)行 Android 的移動(dòng)設(shè)備來說是一個(gè)問題,但是當(dāng) XML 文檔始終保持很小的大小時(shí)是可行的。這可能意味著,Android 的開發(fā)人員會(huì)認(rèn)為 SAX 解析在 Android 應(yīng)用程序上更加常見,因此為它提供了額外的實(shí)用工具。Android 還提供了另一種類型的 XML 解析器,它就是 pull 解析器。
XML pull 解析器
如前所述,Android 并未提供對(duì) Java StAX API 的支持。但是,Android 確實(shí)附帶了一個(gè) pull 解析器,其工作方式類似于 StAX。它允許您的應(yīng)用程序代碼從解析器中獲取事件,這與 SAX 解析器自動(dòng)將事件推入處理程序相反。清單 10 顯示了提要解析接口的一個(gè) pull 解析器實(shí)現(xiàn)。
清單 10. 基于 Pull 解析器的實(shí)現(xiàn)
- public class XmlPullFeedParser extends BaseFeedParser {
- public XmlPullFeedParser(String feedUrl) {
- super(feedUrl);
- }
- public List<Message> parse() {
- List<Message> messages = null;
- XmlPullParser parser = Xml.newPullParser();
- try {
- // auto-detect the encoding from the stream
- parser.setInput(this.getInputStream(), null);
- int eventType = parser.getEventType();
- Message currentMessage = null;
- boolean done = false;
- while (eventType != XmlPullParser.END_DOCUMENT && !done){
- String name = null;
- switch (eventType){
- case XmlPullParser.START_DOCUMENT:
- messages = new ArrayList<Message>();
- break;
- case XmlPullParser.START_TAG:
- name = parser.getName();
- if (name.equalsIgnoreCase(ITEM)){
- currentMessage = new Message();
- } else if (currentMessage != null){
- if (name.equalsIgnoreCase(LINK)){
- currentMessage.setLink(parser.nextText());
- } else if (name.equalsIgnoreCase(DESCRIPTION)){
- currentMessage.setDescription(parser.nextText());
- } else if (name.equalsIgnoreCase(PUB_DATE)){
- currentMessage.setDate(parser.nextText());
- } else if (name.equalsIgnoreCase(TITLE)){
- currentMessage.setTitle(parser.nextText());
- }
- }
- break;
- case XmlPullParser.END_TAG:
- name = parser.getName();
- if (name.equalsIgnoreCase(ITEM) &&
- currentMessage != null){
- messages.add(currentMessage);
- } else if (name.equalsIgnoreCase(CHANNEL)){
- done = true;
- }
- break;
- }
- eventType = parser.next();
- }
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- return messages;
- }
- }
pull 解析器的運(yùn)行方式與 SAX 解析器相似。它提供了類似的事件(開始元素和結(jié)束元素),但您需要使用 (parser.next() 提取它們。事件將作為數(shù)值代碼被發(fā)送,因此您可以使用一個(gè)簡單 case-switch。注意,解析并未像 SAX 解析那樣監(jiān)聽元素的結(jié)束,而是在開始處完成了大部分處理。在 清單 10 的代碼中,當(dāng)某個(gè)元素開始時(shí),您可以調(diào)用 parser.nextText() 從 XML 文檔中提取所有字符數(shù)據(jù)。還需注意,您設(shè)置了一個(gè)標(biāo)記(布爾變量 done)來確定何時(shí)到達(dá)感興趣內(nèi)容的結(jié)束部分。這允許您提早停止讀取 XML 文檔,因?yàn)槟来a將不會(huì)關(guān)心文檔的其余部分。這有時(shí)非常實(shí)用,特別是當(dāng)您只需要訪問一小部分 XML 文檔時(shí)。通過盡快停止解析,您可以極大地減少解析時(shí)間。這種優(yōu)化對(duì)于連接速度較慢的移動(dòng)設(shè)備尤為重要。pull 解析器可以提供一些性能優(yōu)勢(shì)以及易用性。它還可以用于編寫 XML。
創(chuàng)建 XML
目前為止,我一直專注于通過 Internet 解析 XML。但是,有時(shí)您的應(yīng)用程序可能需要將 XML 發(fā)送到遠(yuǎn)程服務(wù)器。顯然,您可以只使用一個(gè) StringBuilder 來創(chuàng)建 XML 字符串。另一種備選方法來自 清單 11 中的 Pull 解析器。
清單 11. 使用 pull 解析器編寫 XML
- private String writeXml(List<Message> messages){
- XmlSerializer serializer = Xml.newSerializer();
- StringWriter writer = new StringWriter();
- try {
- serializer.setOutput(writer);
- serializer.startDocument("UTF-8", true);
- serializer.startTag("", "messages");
- serializer.attribute("", "number", String.valueOf(messages.size()));
- for (Message msg: messages){
- serializer.startTag("", "message");
- serializer.attribute("", "date", msg.getDate());
- serializer.startTag("", "title");
- serializer.text(msg.getTitle());
- serializer.endTag("", "title");
- serializer.startTag("", "url");
- serializer.text(msg.getLink().toExternalForm());
- serializer.endTag("", "url");
- serializer.startTag("", "body");
- serializer.text(msg.getDescription());
- serializer.endTag("", "body");
- serializer.endTag("", "message");
- }
- serializer.endTag("", "messages");
- serializer.endDocument();
- return writer.toString();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
XmlSerializer 類是 前一部分 所使用的 XmlPullParser 包的一部分。它沒有提取事件,而是將它們推出到數(shù)據(jù)流或編寫程序中。在本例中,它僅僅將事件推送到了一個(gè) java.io.StringWriter 實(shí)例中。它提供了一個(gè)直觀的 API,通過各種方法開始和結(jié)束文檔、處理元素以及添加文本或?qū)傩?。這是StringBuilder的一種出色的替換方案,因?yàn)樗梢愿虞p松地確保您的 XML 具有良好結(jié)構(gòu)。
結(jié)束語
您希望為Android設(shè)備構(gòu)建何種類型的應(yīng)用程序?無論如何,如果它需要通過 Internet 使用數(shù)據(jù),那么都可能需要使用XML。在本文中,您看到 Android 提供了大量用于處理XML的工具。您可以選擇其中之一作為自己的工具,或者您可以根據(jù)用例來進(jìn)行選擇。大多數(shù)時(shí)間,使用SAX是比較安全的,并且Android提供了一種傳統(tǒng)的SAX 使用方法,以及一個(gè)便捷的SAX包裝器。如果您的文檔比較小,那么DOM可能是一種比較簡單的方法。如果您的文檔比較大,但您只需要文檔的一部分,則XML Pull解析器可能是更為有效的方法。最后,對(duì)于編寫XML,Pull解析器包也提供了一種便捷的方法。因此,無論您的XML需求如何,Android都能在一定程度上滿足它們。