天天看点

[J2ME]RSSOwlMidlet(RSS无线阅读器)设计说明 1背景、功能和特别之处 2 设计思路 3 设计过程中需要特别指出的问题 4 资源

产品名称

产品版本

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