前段时间开始接触android,看了一遍android自带的sdk文档,感觉很虚,就自己动手写了个简单的rss阅读器来巩固一下学到的知识。主要功能:
支持用户自定义rss类别和每个类别下的频道。通过添加的频道了解该频道下的最新新闻,内置了很多频道,如:javaeye的各版块,名人博客,英语学习,雅虎新闻,新浪新闻等。
首先要了解什么是RSS,关于这个我前面的文章里有解释,这里就不多说了,主要作用是订阅自己感兴趣的新闻,这样就不用一个网站一个网站的去找了,通过RSS阅读器就可以轻松实现实时更新阅读。一个RSS文件就是一个规范的xml数据文件,该文件一般以rss,xml或者rdf作为后缀。下面是一个RSS2.0文件结构:
以下是RSS 2.0的代码样例 <!-- XML版本和字符集 --> <?xml version="1.0"?> <!-- RSS版本 --> <rss version="2.0"> <!-- 以下为频道信息及新闻列表 --> <channel> <!-- 频道总体信息:开始 --> <!-- 频道标题 --> <title>Lift Off News</title> <!-- 频道链接的总地址 --> <link>http://liftoff.msfc.nasa.gov/</link> <!-- 频道描述文字 --> <description>Liftoff to Space Exploration.</description> <!-- 频道使用的语言(zh-cn表示简体中文) --> <language>en-us</language> <!-- 频道发布的时间 --> <pubDate>Tue, 10 Jun 2003 04:00:00 GMT</pubDate> <!-- 频道最后更新的时间--> <lastBuildDate>Tue, 10 Jun 2003 09:41:01 GMT</lastBuildDate> <docs>http://blogs.law.harvard.edu/tech/rss</docs> <!-- 频道生成器 --> <generator>Weblog Editor 2.0</generator> <managingEditor>[email protected]</managingEditor> <webMaster>[email protected]</webMaster> <ttl>5</ttl> <!-- 频道总体信息:结束 --> <!-- 每条RSS新闻信息都包含在item节点中, --> <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> <!-- 新闻目录 --> <category>IT</category> <!-- 新闻作者 --> <author>bill</author> <guid>http://liftoff.msfc.nasa.gov/2003/06/03.html#item573</guid> </item> <item> <title>Space Exploration</title> <link>http://liftoff.msfc.nasa.gov/</link> <description>Sky watchers in Europe, Asia, and parts of Alaska and Canada will experience a partial eclipse of the Sun on Saturday, May 31st.</description> Fri, 30 May 2003 11:06:42 GMT</pubDate> <guid>http://liftoff.msfc.nasa.gov/2003/05/30.html#item572</guid> </item> <item> <title>The Engine That Does More</title> <link>http://liftoff.msfc.nasa.gov/news/2003/news-VASIMR.asp</link> <description>Before man travels to Mars, NASA hopes to design new engines that will let us fly through the Solar System more quickly. The proposed VASIMR engine would do that.</description> Tue, 27 May 2003 08:37:32 GMT</pubDate> <guid>http://www.zhanghangfeng.cn/rss.xml</guid> </item> <item> <title>Astronauts' Dirty Laundry</title> <link>http://liftoff.msfc.nasa.gov/news/2003/news-laundry.asp</link> <description>Compared to earlier spacecraft, the International Space Station has many luxuries, but laundry facilities are not one of them. Instead, astronauts have other options.</description> Tue, 20 May 2003 08:56:02 GMT</pubDate> <guid>http://liftoff.msfc.nasa.gov/2003/05/20.html#item570</guid> </item> </channel> </rss>
?
?我主要是获得item下的title、link、pubDate、description的内容,rss阅读器最核心的是解析rss文件,其实就是解析xml文件。现在流行的有三种解析xml的方式,DOM、SAX、Pull。关于DOM和SAX的解析方式完全不同,DOM是基于文档,而SAX是基于事件驱动的,再移动设备上最好是选择SAX方式,网上有很多关于这方面的资料,读者可以自己去查阅,XmlPullParser和SAX原理差不多,但是更简单。这里采用XmlPullParser方式。解析Rss我主要用到三个类,XmlPullParserUtil.java,RSSFeed.java,RSSItem.java,XmlPullParserUtil是解析RSS文件用的,RSSFeed和RSSItem用来存储解析得到的数据。RSSFeed和一个完整的RSS文件相对应,RSSItem和RSS文件里的item标签相对应。
下面是RSSFeed.java的源码
?
/** * * @author alex *备注:RSSFeed和一个完整的RSS文件相对应 *RSS的描述信息有: *title:标题信息 *link:链接信息 *description:描述信息 */public class RSSFeed { private String title = null; //新闻标题 private String pubdate = null; public String getPubdate() { return pubdate; } public void setPubdate(String pubdate) { this.pubdate = pubdate; } private int itemCount; //新闻数 private List<RSSItem> itemList; //存放的是所有新闻,一个item标签代表一条新闻 public RSSFeed() { itemList = new ArrayList<RSSItem>(); } /** * 负责把一个RSSItem加入到RSSFeed里面 * @param item * @return */ public int addItem(RSSItem item) { itemList.add(item); itemCount ++; return itemCount; } public RSSItem getItem(int location) { return itemList.get(location); } /** * 返回所有的新闻列表 * @return */ public List<RSSItem> getAllItems() { return itemList; } /** * List<Map<String,Object>>存放着要在ListView显示的数据 * @return */ public List<Map<String,Object>> 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 int getItemCount() { return itemCount; }}
?下面是RSSItem.java的源码
?
/** * * @author alex *备注:RSSItem和RSS文件里的item标签相对应 *item包含的信息: *title:标题信息 *link:链接信息 *description:描述信息 *pubDate:发布日期 */public class RSSItem { public static final String TITLE = "title"; //定义两个常量用于显示在listview上的 public static final String PUBDATE = "pubDate"; private String title = null; //新闻标题 private String link = null; //新闻链接 private String pubDate = null; //新闻发布时间 private String description = null; //新闻描述 private String category = null; //新闻类别 public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getLink() { return link; } public void setLink(String link) { this.link = link; } public String getPubDate() { return DateUtil.getSimpleDateFormat(pubDate); //对返回的日期进行格式化,DateUtil是自定义的一个对日期进行格式的类 } public void setPubDate(String pubDate) { this.pubDate = pubDate; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getCategory() { return category; } public void setCategory(String category) { this.category = category; } }
?下面是XmlPullParser.java的源码
?
/** * * @author alex *功能:解析读取的RSS文件,并存放到RSSFeed和RSSItem中 */public class XmlPullParserUtil { public static RSSFeed parseXml(URL url) { RSSFeed rssFeed = new RSSFeed(); RSSItem item = null; try { HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection(); //通过参数url创建一个http远程连接 urlConnection.connect(); //开启连接 InputStream inputStream = urlConnection.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); String str = ""; StringBuffer buffer = new StringBuffer(); while((str = reader.readLine()) != null) { buffer.append(str); //把从网络上读取的数据存进StringBuffer里 } XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); factory.setNamespaceAware(true); //设置为true,则factory创建的XmlPullParser提供对xml 命名空间的支持 XmlPullParser parser = factory.newPullParser(); parser.setInput(new StringReader(buffer.toString())); //解析读取的xml文件流 parser.nextTag(); //此时解析的标签为rss,parser.nextTag():调用next()方法,并返回该事件是START_TAG 还是 END_TAG parser.nextTag(); //此时解析的标签为channel while(parser.nextTag() == XmlPullParser.START_TAG){ String name1=parser.getName(); //获得该标签的名字 if(name1.equals("item")){ item = new RSSItem(); while(parser.nextTag()==XmlPullParser.START_TAG){ String name2 = parser.getName(); if(name2.equals("title")){ item.setTitle(parser.nextText()); }else if(name2.equals("link")){ item.setLink(parser.nextText()); }else if(name2.equals("description")){ item.setDescription(parser.nextText()); } else if(name2.equals("pubDate")) { item.setPubDate(parser.nextText()); } else{ skipUnknownTag(parser); } } rssFeed.addItem(item); }else{ skipUnknownTag(parser); } } }catch(Exception e) { } return rssFeed; } /** * 标签结束时进行的处理 * @param parser * @throws Exception */ private static void skipUnknownTag(XmlPullParser parser) throws Exception { // 事件为START_DOCUMENT时,parser.next()=0 while (parser.next() > 0) { if (parser.getEventType() == XmlPullParser.END_TAG) break; } }}
?以上是该rss阅读器的核心代码,关于android UI的代码由于篇幅就不贴上来了。
通过该项目掌握的知识点:1)RSS的文件结构和含义
?? ? ? ? ? ?2)解析xml文件的几种方式
?? ?3)android应用的结构和MVC模式
?? ?4)activity的生命周期,activity之间通过intent通信,handler消息机制,自定义ListView,LinearLayout、RelativeLayout、FrameLayout布局界面,WebView显示网页。android数据存储(SQLite3)的相关操作,菜单和对话框、Toast的使用,android的测试驱动,uses-permission权限配置等。
下面是该项目在优亿市场的链接
http://www.eoemarket.com/apps/8323
?
附件是源码和截图,写的有些乱,请读者见谅
不知道是不是机器是2.1的缘故。
我看了下里面的代码,是不是要拦截代码还要加些东西?
/** * 监听按键事件 */ public boolean onKeyDown(int keyCode, KeyEvent event) { //按下回退键时 if(keyCode == KeyEvent.KEYCODE_BACK) { showDialog(DIALOG5); } return super.onKeyDown(keyCode, event); }
不知道是不是机器是2.1的缘故。
我看了下里面的代码,是不是要拦截代码还要加些东西?
/** * 监听按键事件 */ public boolean onKeyDown(int keyCode, KeyEvent event) { //按下回退键时 if(keyCode == KeyEvent.KEYCODE_BACK) { showDialog(DIALOG5); } return super.onKeyDown(keyCode, event); }
不好意思哦,这个地方当时没注意到,把return super.onKeyDown(keyCode, event); 改成return true;就ok了。