关于RSS大家应该不会陌生,起码都听说过
同往常一样,这篇文章里没有说教,以实际应用目标,如果你做Android很长时间了,那么你就不用看了,对你来说这都是小儿科,但如果你是新人,这就是要找的文章!
首先做下基础知识的普及
1、RSS是一个特殊的xml,我们看一下它的语法格式:
<?xml version="1.0" encoding="ISO-8859-1"?>
<rss version="2.0">
<channel>
<title>Liftoff News</title> <link>http://liftoff.msfc.nasa.gov/</link> <description>Liftoff to Space Exploration.</description>
<pubDate>Tue, 10 Jun 2003 04:00:00 GMT</pubDate> <item> <title>Star City</title> <link>http://liftoff.msfc.nasa.gov/news/2003/news-starcity.asp</link> <description>How do Americans get ready to work with Russians aboard the International Space Station? They take a crash course in culture, language and protocol at Russia's Star City.</description> <pubDate>Tue, 03 Jun 2003 09:39:21 GMT</pubDate> </item>
<item>
....
</itme>
... </channel> </rss>
简单介绍一下,第一行是xml声明,定义了xml的版本和字符编码
第二行是rss声明,定义了rss版本
第三行<channel>元素用于描述RSS feed
<channel>下有四个元素,其中<title> <link> <description>是<channel>必须有的三个子元素,当然<channel>下还可以有很多子元素,我找的这个例子是比较简单的
<title>标题
<link>链接
<description>描述(一般时候内容)
< pubDate >时间
<channel>可以有多个<item>元素,顾名思义,每个<item>元素都是RSS feed中的一篇文章(如果你英语跟我一样差的话,我解释一下,feed意指饲料,在这里意为资源,至少我是这么理解的!)
<item>元素下也有三个必须的子元素,一样是<title> <link> <description>
xml中的tag都是成对出现的,所以在例子的最后关闭了<channel>和<rss>元素
2、SAX
同DOM一样也是一个访问XML的接口,区别在于他们的工作方式:
DOM将整个文档树存在内存,便于操作,但占资源较多,在pc开发是经常用到
SAX解析XML为事件驱动,通俗理解为用一点拿一点,占资源较少,但你要手动保存数据,在嵌入式开发里有广泛的应用
现在知道为什么要用SAX了吧,因为硬件资源的限制!
基础知识的普及就到此为止,现在我们开始动手了,我们这里以《Android开发入门与实战》提供的范例为例!我们顺下思路,一步一步慢慢来!
对于简单的RSS阅读,很多东西都是固定的,比较简单,但是任何复杂的工程都是通过简单的代码实现的,这是化腐朽为神奇的过程!
还是先看一下api,我们主要用到两个包org.xml.sax和org.xml.sax.helper
第一步,新建两个实体类 RSSFeed 和 RSSItem
我们之前说过,SAX不会将RSS文档都存入内存,我们需要自己保持解析的数据,这两个类就是用来保存解析来的数据,之后的操作主要是操作这两个类
RSSFeed类:
没有什么特别的,无非是对RSS元素标签的定义,直接上代码
package com.rss_reader.data;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import android.R.integer;
public class RSSFeed {
private String title=null;
private String pubdate=null;
private int itemcount=0;
private List<RSSItem> itemlist;
public RSSFeed(){
itemlist=new Vector(0);
}
public int addItem(RSSItem item){
itemlist.add(item);
itemcount++;
return itemcount;
}
public RSSItem getItem(int location){
return itemlist.get(location);
}
public List getAllItems(){
return itemlist;
}
/**
* 获取显示在listView上的所有数据
* @return
*/
public List getAllItemsForListView(){
List<Map<String, Object>> data = new ArrayList<Map<String,Object>>();
int size=itemlist.size();
for(int i=0;i<size;i++){
HashMap<String, Object> item = new HashMap<String, Object>();
item.put(RSSItem.TITLE, itemlist.get(i).getTitle());
item.put(RSSItem.PUBDATE, itemlist.get(i).getPubdate());
data.add(item);
}
return data;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getPubdate() {
return pubdate;
}
public void setPubdate(String pubdate) {
this.pubdate = pubdate;
}
public int getItemcount() {
return itemcount;
}
}
RSSItem类:
这个类里是对<item>元素子元素的定义
package com.rss_reader.data; public class RSSItem { public static final String TITLE = "title"; public static final String PUBDATE = "pubdate"; private String title = null; private String description = null; private String link = null; private String category = null; private String pubdate = null; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getLink() { return link; } public void setLink(String link) { this.link = link; } public String getCategory() { return category; } public void setCategory(String category) { this.category = category; } public String getPubdate() { return pubdate; } public void setPubdate(String pubdate) { this.pubdate = pubdate; } public String toString(){ if(title.length()>20){ return title.substring(0, 42)+"..."; } return title; } }
写完这两个实体类,我们就要对RSS进行解析了,并把解析出来的数据保存到实体类里
第二步 , 自定义继承DefaulttHandler的类,解析RSS的工作就交个它了
这里不得不说一下了,防止有人误会,是不是一定要继承DefaultHandler呢?当然不是!
接下来要说的内容可能不会影响你功能的实现,但如果你想更好的理解,你可以看看!刚刚说过SAX解析xml是基于事件驱动的处理模式,类似UI的事件一样,一定的操作出发一定的事件,像我们点一下按钮产生一个事件,这里,点击按钮是事件源,当我们点击按钮后,事件处理器(监听器)会知道我们点按钮了,并执行相关操作!所有基于实践驱动的处理模式都离不开事件源和处理器(交监听器更形象)
那么在SAX解析过程中什么是事件源,什么是处理器呢?
事件源是org.xml.sax.XMLReader的parse()方法,当这个方法被调用的时候,监听器一直在监听着,无论是解析到xml文档tag头、tag尾、文档的开始、结束...监听器监听到这些操作再做相应的处理
处理器(监听器)是org.xml.sax包下的ContentHandler、DTDHandler、ErrorHandler和EntityResolver四个接口,通过实现这个接口的相关方法实现不同事件的处理,现在我们回到刚才的问题,我们为什么要继承DefaultHandler呢?因为它实现了ContentHandler,也就是说我们的目的不是继承DefaultHandler,而是实现ContentHandler!
先看代码:
package com.rss_reader.sax; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import com.rss_reader.data.RSSFeed; import com.rss_reader.data.RSSItem; public class RSSHandler extends DefaultHandler { RSSFeed rssFeed; RSSItem rssItem; String lastElementName = ""; final int RSS_TITLE = 1; final int RSS_LINK = 2; final int RSS_DESCRIPTION = 3; final int RSS_CATEGORY = 4; final int RSS_PUBDATE = 5; int currentstate = 0; public RSSFeed getFeed() { return rssFeed; } public void startDocument() throws SAXException { rssFeed = new RSSFeed(); rssItem = new RSSItem(); } @Override public void endDocument() throws SAXException { super.endDocument(); }
@Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if ("channel".equals(localName)) { currentstate = 0; return; } else if ("item".equals(localName)) { rssItem = new RSSItem(); return; } else if ("title".equals(localName)) { currentstate = RSS_TITLE; return; } else if ("description".equals(localName)) { currentstate = RSS_DESCRIPTION; return; } else if ("link".equals(localName)) { currentstate = RSS_LINK; return; } else if ("category".equals(localName)) { currentstate = RSS_CATEGORY; return; } else if ("pubDate".equals(localName)) { currentstate = RSS_PUBDATE; return; } currentstate = 0; } @Override public void endElement(String uri, String localName, String qName) throws SAXException { if ("item".equals(localName)) { rssFeed.addItem(rssItem); return; } } @Override public void characters(char[] ch, int start, int length) throws SAXException { String theString = new String(ch, start, length); switch (currentstate) { case RSS_TITLE: rssItem.setTitle(theString); currentstate = 0; break; case RSS_LINK: rssItem.setLink(theString); currentstate = 0; break; case RSS_DESCRIPTION: rssItem.setDescription(theString); currentstate = 0; break; case RSS_CATEGORY: rssItem.setCategory(theString); currentstate = 0; break; case RSS_PUBDATE: rssItem.setPubdate(theString); currentstate = 0; break; default: return; } } }