Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 開發入門 >> 構建 Android 手機 RSS 閱讀器(二)

構建 Android 手機 RSS 閱讀器(二)

編輯:開發入門

android RSS 閱讀器應用程序架構

一份簡短扼要的教程很難詳盡地描述如何構建一個功能豐富的 RSS 閱讀器,因此,我們來看一下 RSS 閱讀器需要實現哪些功能。然後逐步完善重要的組件以實現 XML 處理並呈現到 android 平台。完成這些任務後,您將擁有一個具有基本功能的 RSS 閱讀器,並且具有一些功能鉤子,可以在將來實現進一步擴展。

RSS 閱讀器應用程序的主要需求

以下各小節將詳細介紹 android RSS 閱讀器是如何解決 RSS 閱讀器應用程序的這些主要需求的。

指定感興趣的 RSS 提要

不計其數的 Internet 站點都提供 RSS 提要。應用程序需要指定要處理哪些 RSS 提要。一個功能豐富的 RSS 提要可以使用一種或多種方法選擇所需的 RSS 提,包括能夠從大量站點和通道中進行選擇,或者允許用戶在一個 EditVIEw 中手動輸入感興趣的提要。為了盡量減少本教程中與非 XML 內容相關的代碼,我們對 RSS 提要 URL 進行了硬編碼。將菜單實現為一個鉤子,以便根據需要添加 RSS 提要選擇。

獲取感興趣的 RSS 提要

在對 RSS 提要進行任何解析和數據處理之前,首先需要從 Internet 中獲得它。這意味著需要通過一個 Internet 連接(網絡或 WiFi)連接到托管 RSS 提要的站點,然後執行一個 HTTP GET 操作檢索 RSS 數據。返回的數據並不是文件形式,而是一個 XML 數據流。將使用 URL 類取回數據。

解析 XML 數據流

可以使用多種機制解析 XML 數據。這些機制都需要導航數據流並逐一描述各個數據元素,然後保存這些數據。Android SDK 提供了各種不同的 XML 解析器(Parser) ,但是您也可以選擇自行創建。android SDK 還提供了兩種最流行的方法,即 DOM 解析器和 SAX 解析器。DOM 方法非常適合復雜的 XML 文檔,因為它會在內存中構建面向節點的 XML 數據表示。SAX 方法在遇到新的標記時將使用回調,因而允許應用程序只保存感興趣的數據。由於 RSS XML 結構的簡單性,因此本教程將使用 SAX 解析器。本教程使用了一個RSSHandler 類實現 SAX 解析器回調函數。

保存 RSS 數據

從 XML 數據流提取得到的 RSS 提要必須使用一種有用的格式保存。本教程使用了兩個 helper 類:RSSFeed 和 RSSItem,它們將從 XML 數據流解析得到的數據保存到內存中。當 XML 數據流解析全部完成後,應用程序將與這些類進行交互以呈現信息。

呈現 RSS 提要

本教程的示例應用程序使用兩個 Activity 類提供用戶界面。主屏幕列出了 RSS 提要標題和發布日期,其後是一組 RSS 提要項。當通過一個 tap 選擇某個提要項或將其輸入 android Emulator 後,ShowDescription 活動將顯示有關該提要項的完整信息,包括它的標題(Title)、發布日期(Publication Date)、描述(Description)和鏈接(Link)元素。將設置一個用戶界面,因此,文本中包含的任何鏈接如電子郵件或 Web 都處於激活狀態 — 如果選擇它們,將會發生相應的操作。例如,如果選擇鏈接文本,將啟動 android 浏覽器鏈接到該目標。通過這種方式,RSS 閱讀器可以用非常直觀的方式訪問所有三種級別的信息。這就是 RSS 的強大之處,更具體地說,這就是 RSS 在移動平台中的功能。

數據刷新和離線查看

定期刷新數據是 RSS 閱讀器的一個重要方面,另一個重要功能就是能夠在離線時訪問信息,例如當您在飛機上,您的 android 設備處於飛行模式時。本教程的示例應用程序實現了一些這方面的功能,比如一個用於刷新數據的菜單鉤子,但是本教程沒有討論數據持久性和調度功能。

接下來將構建一個應用程序,獲取一個 RSS 提要並在 android 應用程序中顯示。

構建應用程序

本教程後面的內容將幫助您構建一個 android RSS 閱讀器,主要分兩部分講述。第一部分介紹如何處理 XML 數據流,第二部分站點介紹如何在 android 用戶界面中呈現 RSS 數據。

使用 SAX 獲取並解析 XML 數據

構建 RSS 閱讀器應用程序的核心操作是獲取並處理 XML 數據。我們將通過一個 Internet 連接和一個 HTTP GET 操作獲取 XML 數據。可以使用 Java 中的一些不同的類實現這種 HTTP 處理。本教程演示了如何使用一個 URL 類的實例取回數據。如前面有關應用程序架構的小節所述,本教程將使用 SAX 解析器。如果數據格式相對簡單,或者您的數據需求允許選擇性地使用數據結構,那麼使用 SAX 解析器僅需要最少量的內存和 Excel。

在深入研究 SAX 解析器的具體應用之前,請首先快速查看一下RSSFeed 和 RSSItem 類,因為本教程後面的內容將大量提到這兩個類。

RSSFeed

RSSFeed 類從一個較高級別的角度表示 RSS 提要。RSSFeed 類包含 RSS 數據源的通道部分中感興趣的元素,還包含一組RSSItemRSSItem 表示 RSS 通道中的單個項。觀察一下這兩個重要的類。RSSFeed.Java 如清單 4 所示。


清單 4. RSSFeed.Java

                    
package com.msi.androidrss;


import java.util.List;
import Java.util.Vector;
import com.msi.androidrss.RSSItem;

public class RSSFeed 
{
    private String _title = null;
    private String _pubdate = null;
    private int _itemcount = 0;
    private List<RSSItem> _itemlist;
    
    
    RSSFeed()
    {
        _itemlist = new Vector(0); 
    }
    int addItem(RSSItem item)
    {
        _itemlist.add(item);
        _itemcount++;
        return _itemcount;
    }
    RSSItem getItem(int location)
    {
        return _itemlist.get(location);
    }
    List getAllItems()
    {
        return _itemlist;
    }
    int getItemCount()
    {
        return _itemcount;
    }
    void setTitle(String title)
    {
        _title = title;
    }
    void setPubDate(String pubdate)
    {
        _pubdate = pubdate;
    }
    String getTitle()
    {
        return _title;
    }
    String getPubDate()
    {
        return _pubdate;
    }
}

 

當 SAX 解析引擎在 RSS 源中完成了 XML 數據解析後,將創建一個RSSFeed 類的實例,現在它包含了在您的應用程序中處理 RSS 數據所需的所有內容。RSSFeed 類包含三個重要元素,以及用於數據處理的 Set-ers 和 Get-ers。這些感興趣的數據元素包括:

  • Title (_title):使用一個 Java.lang.String 保存通道的標題以便進行顯示。
  • Publication Date (_pubdate):使用一個 Java.lang.String 保存發布日期以便進行顯示。注意,在更復雜的 RSS 閱讀器中,您可以使用發布日期信息執行智能更新或刷新操作。
  • List of Items (_itemlist):一個參數化的 Java.util.List,用於保存 RSSItem 集合。

RSSItem

讓我們看一看 RSSFeed 中包含的項列表。該列表中的每個元素都屬於 RSSItem 類型。RSSItem 類定義了一些 java.lang.String 類型來保存解析器獲得的值。將每個元素放入一個方便的檢索類中,這使應用程序邏輯和用戶界面的呈現變成了一個非常簡單的任務,您稍後就會體會到。RSSItem.Java 如清單 5 所示。


清單 5. RSSItem.Java

                    
package com.msi.androidrss;

public class RSSItem 
{
    private String _title = null;
    private String _description = null;
    private String _link = null;
    private String _category = null;
    private String _pubdate = null;

    
    RSSItem()
    {
    }
    void setTitle(String title)
    {
        _title = title;
    }
    void setDescription(String description)
    {
        _description = description;
    }
    void setLink(String link)
    {
        _link = link;
    }
    void setCategory(String category)
    {
        _category = category;
    }
    void setPubDate(String pubdate)
    {
        _pubdate = pubdate;
    }
    String getTitle()
    {
        return _title;
    }
    String getDescription()
    {
        return _description;
    }
    String getLink()
    {
        return _link;
    }
    String getCategory()
    {
        return _category;
    }
    String getPubDate()
    {
        return _pubdate;
    }
    public String toString()
    {
        // limit how much text you display
        if (_title.length() > 42)
        {
            return _title.substring(0, 42) + "...";
        }
        return _title;
    }
}

 

RSSItem 類的實例包含與 RSS 提要中單個項有關的文本數據元素。除了用於保存數據的 Java.lang.String 字段外,該類還為每個字段包含了一個 Get-er 和 Set-er。最後,該類覆蓋了 toString() 方法,因為當 android 用戶界面元素顯示項列表時將調用這個方法。請牢記關於 toString() 方法的注釋,因為在討論在 android 用戶界面呈現 RSS 數據時將使用到。

SAX 方法

如上文所述,使用 SAX 方法解析 XML 依賴一種回調結構,在這種結構中,解析器將掃描數據流以查找標記,並在處理程序(由您提供)中調用方法來處理數據。該處理程序擴展了一個稱為DefaultHandler 的 Java 類。處理程序中有五種相關的方法。在 XML 解析處理中,每種方法都執行一個單獨的、截然不同的重要角色。這五種方法分別是:

  • startDocument:當開始解析文檔時調用。它將初始化所需的數據結構。
  • endDocument:在結束文檔解析時調用。
  • startElement:當掃描器發現一個新標記時調用。處理程序通常使用這個方法確定文檔中的位置,以便為保存數據做好准備。此外,與該元素相關的任何屬性都可用於處理、保存和解釋。
  • endElement:當掃描器遇到一個結束標記時調用。處理程序通常使用這種方法判斷文檔位置並保存中間過程產生的數據。在 RSS 示例應用程序中,當遇到 </item> 標記時,每個 RSSItem 被保存到RSSFeed 對象。
  • characters:當標記中的數據變為可用時調用。例如,在解析 XML 數據時,標題、描述、鏈接、類別和發布日期元素的內容都被保存在這個方法中。

RSSHandler

RSSHandler 類是運行本教程應用程序的關鍵。它負責識別並保存 XML 數據流中的有關信息。當 SAX 解析器調用時,將依次調用它的每個方法。

RSSHandler 實現一些非常基本的狀態處理,從而在 SAX 解析器處理 XML 數據流時正確地識別和保存數據。SAX 方法可以使用多種正確的方式管理狀態。雖然與本教程介紹的 RSS 提要解析主題沒有必然聯系,但是,如果您覺得在解析某一特定 XML 數據源時,使用 SAX 管理狀態非常復雜,則可以考慮使用 DOM 解析器。DOM 解析器的工作前提是文檔中的所有元素都非常重要,並且您的應用程序需要檢測並提取 XML 數據源中的各種元素。SAX 非常適合 RSS 提要,因為 RSS 的關系非常簡單。清單 6 展示了 RSSHandler.Java 的實現。請迅速查看一下這些方法,以熟悉處理程序的結構。


清單 6. RSSHandler.Java

                    
package com.msi.androidrss;

import org.xml.sax.helpers.DefaultHandler;
import org.XML.sax.*;
import android.util.Log;


public class RSSHandler extends DefaultHandler 
{
    
    RSSFeed _feed;
    RSSItem _item;
    String _lastElementName = "";
    boolean bFoundChannel = false;
    final int RSS_TITLE = 1;
    final int RSS_LINK = 2;
    final int RSS_DESCRIPTION = 3;
    final int RSS_CATEGORY = 4;
    final int RSS_PUBDATE = 5;
    
    int depth = 0;
    int currentstate = 0;
    /*
     * Constructor 
     */
    RSSHandler()
    {
    }
    
    /*
     * getFeed - this returns our feed when all of the parsing is complete
     */
    RSSFeed getFeed()
    {
        return _feed;
    }
    
    
    public void startDocument() throws SAXException
    {
        // initialize our RSSFeed object - this will hold our parsed contents
        _feed = new RSSFeed();
        // initialize the RSSItem object - you will use this as a crutch to grab 
		// the info from the channel
        // because the channel and items have very similar entrIEs..
        _item = new RSSItem();

    }
    public void endDocument() throws SAXException
    {
    }
    public void startElement(String namespaceURI, String localName,String qName, 
                                             Attributes atts) throws SAXException
    {
        depth++;
        if (localName.equals("channel"))
        {
            currentstate = 0;
            return;
        }
        if (localName.equals("image"))
        {
            // record our feed data - you temporarily stored it in the item :)
            _feed.setTitle(_item.getTitle());
            _feed.setPubDate(_item.getPubDate());
        }
        if (localName.equals("item"))
        {
            // create a new item
            _item = new RSSItem();
            return;
        }
        if (localName.equals("title"))
        {
            currentstate = RSS_TITLE;
            return;
        }
        if (localName.equals("description"))
        {
            currentstate = RSS_DESCRIPTION;
            return;
        }
        if (localName.equals("link"))
        {
            currentstate = RSS_LINK;
            return;
        }
        if (localName.equals("category"))
        {
            currentstate = RSS_CATEGORY;
            return;
        }
        if (localName.equals("pubDate"))
        {
            currentstate = RSS_PUBDATE;
            return;
        }
        // if you don't explicitly handle the element, make sure you don't wind 
               // up erroneously storing a newline or other bogus data into one of our 
               // existing elements
        currentstate = 0;
    }
    
    public void endElement(String namespaceURI, String localName, String qName) 
                                                               throws SAXException
    {
        depth--;
        if (localName.equals("item"))
        {
            // add our item to the list!
            _feed.addItem(_item);
            return;
        }
    }
     
    public void characters(char ch[], int start, int length)
    {
        String theString = new String(ch,start,length);
        Log.i("RSSReader","characters[" + theString + "]");
        
        switch (currentstate)
        {
            case RSS_TITLE:
                _item.setTitle(theString);
                currentstate = 0;
                break;
            case RSS_LINK:
                _item.setLink(theString);
                currentstate = 0;
                break;
            case RSS_DESCRIPTION:
                _item.setDescription(theString);
                currentstate = 0;
                break;
            case RSS_CATEGORY:
                _item.setCategory(theString);
                currentstate = 0;
                break;
            case RSS_PUBDATE:
                _item.setPubDate(theString);
                currentstate = 0;
                break;
            default:
                return;
        }
        
    }
}

 

RSSHandler 解釋

接下來進一步查看一下 RSSHandler 類。注意,該類具有一個RSSFeed 類的實例。RSSHandler 類的作用是從 SAX 解析器實現回調,並在此過程中形成 RSS 數據的表示以供應用程序使用。

startElement 方法指定找到了哪些數據元素,而 characters 方法通過相應的 set 方法向其中一個 RSSItem 成員執行分配。endElement 檢查 item 元素的末尾,如果發現數據元素,則將當前的 RSSItem 添加到 RSSFeed

RSSHandler 被設計為自包含形式,以進行 SAX 解析。它的方法都可以響應解析器的事件,構建 RSSFeed,該類隨後通過 getFeed 方法使完全填充的 RSSFeed 對象變為可用。

設置 SAX

現在您大致了解了在進行 SAX 解析時發生的操作,現在查看一下 SAX 解析器的調用。相關的代碼位於 RSSFeed 類的 getFeed() 方法中,如清單 7 所示。


清單 7. RSSFeed.Java 中的 getFeed() 方法

                    
    private RSSFeed getFeed(String urlToRssFeed)
    {
        try
        {
            // setup the url
           URL url = new URL(urlToRssFeed);

           // create the factory
           SAXParserFactory factory = SAXParserFactory.newInstance();
           // create a parser
           SAXParser parser = factory.newSAXParser();

           // create the reader (scanner)
           XMLReader xmlreader = parser.getXMLReader();
           // instantiate our handler
           RSSHandler theRssHandler = new RSSHandler();
           // assign our handler
           xmlreader.setContentHandler(theRssHandler);
           // get our data through the url class
           InputSource is = new InputSource(url.openStream());
           // perform the synchronous parse           
           XMLreader.parse(is);
           // get the results - should be a fully populated RSSFeed instance, 
		   // or null on error
           return theRssHandler.getFeed();
        }
        catch (Exception ee)
        {
            // if you have a problem, simply return null
            return null;
        }
    }

 

查看 清單 7 中的代碼,可以看到,對 SAX 解析器和您的RSSHandler 類必需的類進行了實例化。當將 RSSHandler 分配給 XMLReader 實例後,就可以開始進行解析。注意,SAX 中最困難的工作就是定義您的處理程序!但是在可以解析數據之前,您必須對數據進行檢索。

獲取 XML 數據

獲取 XML 數據流的 HTTP 事務通過 URL 類實現,將它的 Stream 傳遞給 InputSource 類的新實例。SAX 解析器/掃描器在分配的處理程序內調用各種方法,使用 InputSource 導航數據流並執行解析操作。在本例中,這些方法都位於 RSSHandler 類中。一旦解析完成,將從 RSSHandler 中檢索 RSSFeedRSSHandler 在每次回調操作期間構建一個 RSSFeed 類的實例。

try/catch 塊中將嘗試一個解析操作,如果操作成功,將返回一個RSSFeed 的實例。如果發生錯誤,將拋出異常並且函數返回 null。

當一個完整的 RSSFeed 類的實例可用後,將開始向用戶呈現數據。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved