天天看點

[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