RSS阅读器制作思路与步骤

关于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; } } }

简单解释一下这个类的代码:
startDocument()通常我们把初始化的工作放到这里
endDocument()收尾的工作放到这个方法里(本例中什么也没做)

public void startElement(String uri, String localName, String qName,Attributes attributes) throws SAXException
每当解析到x ml里的tag的时候,这个方法会被调用,所以在这个方法里通常判断localName是做不同的操作,而每当localName是item的时候,就要新建item实体类对象,要来保存item解析的内容
public void characters(char[] ch, int start, int length)
执行完 startElement 方法后会调用这个方法,参数ch[]就是节点内容,我们根据currentstate不同,判断ch[]是哪个节点的,并把内容防止到合适的实体类里
endElement   和 startElement 对应,解析完一个tag后调用这个方法,没什么好说的,不过如果是item节点的结束,就要把RSSItem添加到RSSFeed里
到这里,解析的工作已经完成了,下面我们把它们显示出来!
第三步 ,显示数据
这就不想多说了,无非是解析rss后显示到列表上,给列表加个事件,点击显示内容...比价简单,看代码吧
package com.rss_reader; import java.net.URL; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.InputSource; import org.xml.sax.XMLReader; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import android.widget.ListView; import android.widget.SimpleAdapter; import android.widget.AdapterView.OnItemClickListener; import com.rss_reader.data.RSSFeed; import com.rss_reader.data.RSSItem; import com.rss_reader.sax.RSSHandler; public class ActivityMain extends Activity implements OnItemClickListener{ /** Called when the activity is first created. */ public final String RSS_URL = "http://blog.sina.com.cn/rss/nandush.xml"; public final String tag = "RSSReader"; private RSSFeed feed = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); feed = getFeed(RSS_URL); showListView(); } private RSSFeed getFeed(String urlString) { try {
URL url = new URL(urlString); SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser parser = factory.newSAXParser(); XMLReader xmlReader = parser.getXMLReader(); RSSHandler rssHandler = new RSSHandler(); xmlReader.setContentHandler(rssHandler); InputSource is = new InputSource(url.openStream()); xmlReader.parse(is); return rssHandler.getFeed(); } catch (Exception e) { e.printStackTrace(); return null; } } private void showListView() { ListView itemlist = (ListView) findViewById(R.id.itemlist); if (feed == null) { setTitle("访问的rss无效"); return; } SimpleAdapter adapter = new SimpleAdapter(this, feed.getAllItemsForListView(), android.R.layout.simple_list_item_2, new String[] { RSSItem.TITLE, RSSItem.PUBDATE }, new int[] { android.R.id.text1, android.R.id.text2 }); itemlist.setAdapter(adapter); itemlist.setOnItemClickListener(this); itemlist.setSelection(0); } public void onItemClick(AdapterView<?> parent, View v, int position, long id) { Intent itemIntent = new Intent(this,ActicityShowDescription.class); Bundle b=new Bundle(); b.putString("title", feed.getItem(position).getTitle()); b.putString("description", feed.getItem(position).getDescription()); b.putString("link", feed.getItem(position).getLink()); b.putString("pubdate", feed.getItem(position).getPubdate()); itemIntent.putExtra("android.intent.extra.rssitem", b); startActivityForResult(itemIntent, 0); } }
这段代码里唯一需要解释的是:
URL url = new URL(urlString);
                 SAXParserFactory factory = SAXParserFactory.newInstance();
                 SAXParser parser = factory.newSAXParser();
                 XMLReader xmlReader = parser.getXMLReader();
                 RSSHandler rssHandler = new RSSHandler();
                 xmlReader.setContentHandler(rssHandler)   ;
                 InputSource is = new InputSource(url.openStream());
                 xmlReader.parse(is);
回忆 一下刚才提到的事件源,就是这段代码实现的这段代码基本上是固定的...
点击列表项跳转的界面应该都会写了吧!

写的手都累了,最后要感激《Android开发入门与实践》的作者,用别人的例子写自己的总结,让大家见笑了!
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值