产品名称
产品版本
keyword: rssreader rssfeed channel j2me midp midlet kxml xmlpull rms rssowl java
rss无线阅读器
0.7.1729
[j2me][开源]rssowlmidlet
(rss无线阅读器)
设计说明
我的rssreader资源:
本文档给出rss无线阅读器j2me版本的设计思路和类说明。
第1章 简单描述rss无线阅读器j2me版本的背景、功能和特别之处。
第2章 描述调用rss无线阅读器j2me版本的设计思路。
第3章 给出了 rss无线阅读器j2me版本的设计过程中需要特别指出的问题。
第4章 给出了 各种下载资源。
摘要:本章 简单描述rss无线阅读器j2me版本的背景、功能和特别之处。
我们提供的rss无线阅读器j2me版本[开源]是一个可以下载到手机(例如nokia7610)或者其他无线手持设备(例如pocketpc)的应用程序,用来阅读存在于internet中广泛的rss新闻源,比如
等等,这些新闻源我们将预置在本应用程序中。
下面介绍rss无线阅读器的功能列表:
l rss feed列表
n 添加rss feed
n 编辑rss feed
n 删除rss feed
l 读取rss feed新闻
n 查看新闻摘要
l 重新读取rss feed新闻
l 关于我
这里简单介绍一下rss,它并不是一个新概念,从大约2002年就已经通行天下了,最通常的是blog的rss输出,当然许多其他类型的站点也都提供了本站内容的rss输出格式,比如:
新闻站点,比如“百度新闻”或者“google新闻”;
门户,比如“新浪体育”,等等。
rss其实就是特定格式的xml。所以在j2me解决方案中,就是通过httpconnection获取rss xml文档并解析,将新闻条目显示在界面上,说起来很简单的。
下面,我们说说这个应用程序的特别之处。
这个应用程序的思路来自于两个开源的应用程序:
rss读取以及用xmlpull解析这部分思路,采用了tommi 的思路;
mvc的类分解,以及对rms记录存储的封装模式,采用了 garrey 的思路。
另外,在调试过程中,也加入了我的一些思考,以及兼容各种rss的办法。
总结一下特别之处。
特别是比原来tommi 的版本增加了几个特性:
l 最大的改动就是,原来tommi把视图/控制器都放在rssreadermidlet.java了,而我们将视图分拆出来为rssfeedadd.java、rssfeededit.java、rssfeedlist.java等等,控制器则为guicontroller.java;
l 可以添加/编辑/删除rss feed书签;
l 原来tommi将feed列表以及url存储在rms的一个条目,我放弃了这种做法,而是像garrey处理电话记录一样;
l 当请求远端服务器时,加入了动画等候画面,提示用户正在获取新闻列表;
l 由于采用了xmlpull方式解析xml,所以可以做到一边后台扫描xml文档,一边前台将解析到的新闻title显示在用户界面上,不影响用户阅读新闻列表;
l 兼容博客堂/博客园这种.text类型blog的rss,因为它们限制请求方的“user-agent”;
l 采用kxmlparser的自动检测rss xml文档的编码格式,所以不用用户专门设置编码格式。
摘要:本章描述rss无线阅读器j2me版本的设计思路。
关键三个包:
l javax.microedition.io. httpconnection;
l org.kxml2.io. kxmlparser;
l org.xmlpull.v1.*。
httpconnection:
下面来看看如何用httpconnection类来请求远端的rss服务器并获取rss xml数据。
第一步是使用connector类打开一个到服务器的连接,我们将把这个连接强制转换为需要的httpconnection类型。
代码
httpconnection hc = null;
hc = (httpconnection) connector.open( url );
hc.setrequestmethod(httpconnection.get);
接下来,我们得到httpconnection上的一个inputstream,允许我们一个字符一个字符的读取服务器的响应数据。
parserssfeedxml( hc.openinputstream() );
public void parserssfeedxml(inputstream is);
org.kxml2.io. kxmlparser:
parserssfeedxml函数就是负责用kxmlparser来解析这响应数据。
enhydra的kxml是一个被设计用于j2me设备的只占很小存储空间的xml语法分析程序,虽然它也可以被用于其它需要小型xml语法分析程序的环境,比如applet。kxml支持以下特性:
1、支持xml名称空间;
2、用"松散"模式分析html或其它sgml格式;
3、占用很少的存储空间(21 kbps;
4、基于pull的分析;
5、支持xml写操作;
6、可选的dom支持;
7、可选的wap支持。
kxml支持dom语法分析和操作,但是不支持push语法分析。取而代之,它使用一种稍微不同的称为“pull”的分析方法。与push语法分析相反,pull语法分析让程序员从语法分析程序中“拉”出下一个事件。
优点:不必等整个文档解析完成,部分求值结果早就可以开始反馈给用户。
下面我们看看kxml如何做一个pull语法分析程序。
首先我们需要创建了一个xmlparser,并把它传到一个inputstream中。
public void parserssfeedxml(inputstream is)
throws ioexception, xmlpullparserexception {
kxmlparser parser = new kxmlparser();
parser.setinput( is, null); // 设置null让kxmlparser自动检测该使用哪种编码
其次,我们需要跳过rss根节点的“rss”之类的东西。
parser.nexttag();
parser.require(parser.start_tag, null, null);
下面,我们来寻找rss中的第一个“item”节点,它代表这个rss文档中确实包含了新闻条目。这是通过下面的循环做到的。parser.next()的含义是“get next parsing event”,这样就可以遍历文档,如果找不到item节点,就抛出一个异常。
while(!"item".equals(parser.getname()) ){
/** check if document doesn't include any item tags */
if( parser.next() == parser.end_document )
throw new ioexception("no items in rss feed!");
}
确认有item节点后,我们来寻找rss中代表每一个新闻的“item”节点下的三个节点“title”“link”“description”,这是通过下面的循环做到的。parser.next()的含义是“call next() event if it is start_tag or end_tag otherwise throw an exception”,这样就可以遍历文档找全所有的新闻的主题、链接和摘要了。
/** parse <item> tags */
do {
parser.require(parser.start_tag, null, null);
/** initialize properties */
title = "";
description = "";
link = "";
/** one <item> tag handling*/
while (parser.nexttag() != parser.end_tag) {
parser.require(parser.start_tag, null, null);
string name = parser.getname();
string text = parser.nexttext();
/** save item property values */
if (name.equals("title"))
title = text;
else if (name.equals("description"))
description = text;
else if (name.equals("link"))
link = text;
parser.require(parser.end_tag, null, name);
}
/** create new rss item and add it do rss document's item
* collection
*/
rssitem rssitem = new rssitem(title, link, description);
m_rssfeed.getitems().addelement( rssitem );
parser.nexttag();
} while("item".equals(parser.getname()));
划分为四个模块:
l midlet:
n rssreadermidlet.java:
u 这当然是midlet必须的入口;它负责初始化控制器guicontroller
l model,模型:
n rssfeed.java
u rss feed所对应的类实体,基本上有这么几个属性:
l 在rms中存储的id序号;
l feed名称,如“博客堂”或“博客园”;
n rssfeedparser.java
u 获取rss xml并解析的类
n rssitem.java
u 对应于rss feed返回的每一个新闻条目的类实体,包含主题、摘要以及链接;
l view,视图:
n about.java
u “关于我”的alert界面
n rssfeedadd.java
u “添加rss feed书签”的form界面
n rssfeededit.java
u “编辑rss feed书签”的form界面
n rssfeedlist.java
u “rss feed列表”的form界面
n rssfeedopen.java
u “读取rss feed新闻”的form界面
n rssfeedview.java
u “查看rss feed书签”的form界面
n rssitemview.java
u “查看新闻摘要”的form界面
n waitflash.java
u “动画等待画面”的canvas界面
l controller:
n guicontroller.java
u mvc中的控制器部分,负责界面事件的处理,以及决定该显示哪一个form
控制器的事件处理部分:
下面来看看如何处理界面事件。
public void handleevent( int eventid,object[] args){
switch (eventid)
{
case eventid.event_exit:
{
system.gc(); // 通知进行垃圾收集
thread.yield(); // 本线程暂停一下,使得gc可以马上获得机会运行
rssmidlet.exit(false);
break;
}
case eventid.event_view_detail:
setcurrent(openform);
openform.setrss((rssfeed)args[0]);
break;
……
每一个form的如何转发事件:
每一个form上也有事件响应,并可以自行处理事件,然后加入参数,进一步转发事件到控制器上。这个概念来自于garrey的手机电话本源代码。
/*
* 内部监听器,监听器监听所有command事件,并把事件响应推出来让控制器处理
private class rssfeedaddlistener implements commandlistener{
public void commandaction(command command, displayable disp){
if(command == back_command){
controller.handleevent(guicontroller.eventid.event_new_back, null);
else if(command == save_command){
string title = titlefield.getstring();
string url = urlfield.getstring();
if((title == null || title.equals(""))
|| (url == null || url.equals("") || url.equals("http://"))){
return;
}
object[] args = {title, url};
controller.handleevent(guicontroller.eventid.event_new_save, args);
}//end else
}
}//end inner class
我们在rssfeedopen这个“读取rss feed新闻”的form界面中,做了一点特殊处理,这样才能够后台线程专门读取rss xml,而前台界面只需要负责跟进显示解析出来的rss新闻条目即可了。
所以我们的类这么声明:
public class rssfeedopen extends form
implements runnable
我们为了实现,特地实现了一个run函数,它不断地循环查看标志m_getpage,如果是true,则说明后台线程应该去获取rss xml了。否则,就睡眠一段时间。
public void run(){
/* use networking if necessary */
long lngstart;
long lngtimetaken;
while(true) {
try {
// 我们是依靠m_getpage来判断是否去得到并解析rss xml的.
// 如果当前m_getpage是false,那么本线程就只能先睡眠一段时间了.
if( m_getpage ) {
try {
/** get rss feed */
m_currssparser.parserssfeed();
setcurrentdispalytoheaderlist();
}catch(exception e) {
}
m_getpage = false;
}
lngstart = system.currenttimemillis();
lngtimetaken = system.currenttimemillis() - lngstart;
if(lngtimetaken < 100)
m_netthread.sleep(75 - lngtimetaken);
} catch (interruptedexception e) {
break;
}
}
摘要:本章给出了 rss无线阅读器j2me版本的设计过程中需要特别指出的问题。
在请求rss feed时,遇到一个奇怪的问题。别的rss源都没有问题,唯独博客堂/博客园始终返回这样的错误信息:
bad request (invalid header name)
看来是httpconnection请求时的header设置问题。
最终经过反复试验,发现不能设置“user-agent”字段,否则对方.text应用不接受。
所以我把下面的代码注释:
hc.setrequestproperty("user-agent",
"profile/midp-1.0 configuration/cldc-1.0");
tommi的代码中,是直接用:
parser.setinput( is,"utf-8"); // 设置读取用utf-8编码
来解析xml的。这样会有问题。
后来看到kxml2的kxmlparser.java中setinput函数其实是可以自动处理编码问题的:
public void setinput(inputstream is, string _enc){
….
if (enc == null) {
// read four bytes
int chk = 0;
while (srccount < 4) {
int i = is.read();
if (i == -1)
break;
chk = (chk << 8) | i;
srcbuf[srccount++] = (char) i;
if (srccount == 4) {
switch (chk) {
case 0x00000feff :
enc = "utf-32be";
srccount = 0;
break;
case 0x0fffe0000 :
enc = "utf-32le";
case 0x03c :
srcbuf[0] = '<';
srccount = 1;
case 0x03c000000 :
case 0x0003c003f :
enc = "utf-16be";
srcbuf[1] = '?';
srccount = 2;
case 0x03c003f00 :
enc = "utf-16le";
网络资源:
6:实例教您kxml:j2me中xml语法分析的利器
编写者
日期
关键词
郑昀@ultrapower
2005-10-07
rssreader rssfeed channel
j2me midp midlet
kxml xmlpull
rms
rssowl
java