産品名稱
産品版本
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