de >RssHandlerde >类为 SAX 解析器生成的事件所对应的所有方法都提供了一个默认的非操作实现。这允许子类根据需要仅覆盖一些方法。de >RssHandlerde > 提供了一个额外的 API,即 de >getMessagesde >。它返回处理程序在从 SAX 解析器接收事件时所收集的 de >Messagede > 对象列表。
de >RssHandlerde > 类扩展了 de >org.xml.sax.helpers.DefaultHandlerde > 类。该类为 SAX 解析器生成的事件所对应的所有方法都提供了一个默认的非操作实现。这允许子类根据需要仅覆盖一些方法。de >RssHandlerde > 提供了一个额外的 API,即 de >getMessagesde >。它返回处理程序在从 SAX 解析器接收事件时所收集的 de >Messagede > 对象列表。它有另外两个内部变量,de >currentMessagede > 针对被解析的 de >Messagede > 实例,以及名称为 de >builderde > 的 de >StringBuilderde > 变量,用于存储文本节点中的字符数据。解析器将相应事件发送给处理程序时会调用 de >startDocumentde > 方法,这两个变量的初始化操作就是在此时完成。
查看de >startElementde > 方法。在XML文档中每次遇到开始标记时都会调用它。您只关心该标记何时为 de >ITEMde > 标记。对于这种情况,您将创建一个新的 de >Messagede >。现在来看 de >charactersde > 方法。遇到文本节点中的字符数据时便会调用此方法。数据只是被添加到 de >builderde > 变量中。最后,我们来看 de >endElementde > 方法。遇到结束标记时会调用此方法。对于与某 de >Messagede > 属性相对应的标记,如 de >TITLEde > 和 de >LINKde >,则使用 de >builderde > 变量中的数据在 de >currentMessagede > 上设置适当的属性。如果结束标记是一个 de >ITEMde >,则 de >currentMessagede > 将被添加到 Messages 列表中。所有这些都是非常典型的 SAX 解析;此处的一切都不是 Android 所特有的。因此,如果您知道如何编写 Java SAX 解析器,则应该知道如何编写 Android SAX 解析器。但是,android sdk 确实在 SAX 上添加了一些便捷的特性。
Android SAX 解析器
java代码:
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();
}
}
注意,这个类仍然使用了一个标准的 SAX 处理程序,因此您仅仅重用了所示的 de >RssHandlerde >。能够重用 SAX 处理程序是非常不错的,但其代码稍微有些复杂。您可以想像,如果需要解析一个更加复杂的 XML 文档,则处理程序可能会带来各种各样的 bug。举例来说,回头看看 de >endElementde > 方法。注意,在尝试设置属性之前,它检查了 de >currentMessagede > 是否为 null。现在,再回头看看示例 XML。 注意,de >ITEMde > 标记外部有一些 de >TITLEde > 和 de >LINKde > 标记。这就是使用 null 检查的原因。否则,每一个 de >TITLEde > 标记 会导致一个 de >NullPointerExceptionde >。Android 提供了自己独有的 SAX API,它排除了您编写自己的 SAX 处理程序的需要。
经过简化的 Android SAX 解析器
java代码:
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;
}
}
所有代码都是可选的。如果您习惯Java环境中的标准SAX解析代码,那么您可以坚持使用它。如果您希望尝试android sdk所提供的便捷的包装器,那么也可以使用它。如果您完全不希望使用SAX会怎样呢?可以使用一些备选方案。其中的首选方法就是 DOM。Android完全支持DOM解析,就像在桌面机器或服务器上使用Java代码运行它一样。
新的 SAX 解析代码并未使用 SAX 处理程序,而是使用了 SDK 中的 android.sax 包中的类。这些类允许您构建 XML 文档的结构,并根据需要添加事件监听程序。在以上代码中,您声明文档将有一个 de >rssde > 根元素,并且它有一个 de >channelde > 子元素。然后,您声明 de >channelde > 将有一个 de >ITEMde > 子元素,并且开始添加监听程序。对于每个监听程序,您都使用了一个实现了特定接口(de >EndElementListnerde > 或 de >EndTextElementListenerde >)的匿名内部类。注意,您不需要跟踪字符数据。不仅仅因为这样会更加简单,更重要的是更加高效。最后,在调用 Xml.parse 实用方法时,您将传递一个通过根元素生成的处理程序。
所有代码都是可选的。如果您习惯 Java 环境中的标准 SAX 解析代码,那么您可以坚持使用它。如果您希望尝试 android sdk 所提供的便捷的包装器,那么也可以使用它。如果您完全不希望使用 SAX 会怎样呢?可以使用一些备选方案。其中的首选方法就是 DOM。
Android 完全支持 DOM 解析,就像在桌面机器或服务器上使用 Java 代码运行它一样。显示了一个基于 DOM 的解析器接口实现。
基于 DOM 的提要解析器实现
java代码:
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;
}
}