Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Build a mobile RSS reader

Handle XML with Android

Frank Ableson is an entrepreneur and software developer in northern New Jersey, specializing in mobile and embedded application software. He is currently authoring a book about Android application development for Manning Publications. His professional interests are embedded systems, wireless communications, and automotive electronics. His biggest fans are his wife, Nikki, and their children.

Summary:  What good is a mobile computing environment if you can't keep track of your favorite news feeds on the go? Sure, you can use Android's browser to read your favorite Web sites, but it's unlikely the sites are optimized for a screen two inches high. And besides, then you'll miss the opportunity to integrate RSS or other XML data with other mobile applications to make your own mash-ups. This tutorial shows you how to use the Android Developer Tools to read, parse, and display XML data.

Date:  18 Mar 2008
Level:  Intermediate PDF:  A4 and Letter (328 KB | 33 pages)Get Adobe® Reader®

Activity:  176311 views
Comments:  

Building the application

The remainder of the tutorial helps you build the Android RSS reader. The explanations are broken into two main sections. The first section discusses the handling of XML data streams, while the following section focuses on the rendering of the RSS data in the Android user interface.

Fetching and parsing XML data with SAX

The core activity in building an RSS reader application is the retrieval and handling of XML data. Fetching XML data is done through an Internet connection and can be accomplished through an HTTP GET operation. A few different classes in Java enable this kind of HTTP transaction. This tutorial demonstrates fetching data with an instance of the URL class. As discussed briefly in the earlier section on application architecture, the SAX parser is employed in this tutorial. SAX parsers require minimal amounts of memory and excel when the data formats are relatively simple in nature or when your data requirements allow selective use of the data structure.

Before you dive into the specifics of using the SAX parser, take a quick look at the RSSFeed and RSSItem classes as they will be referred to extensively throughout the remainder of this tutorial.


RSSFeed

The RSSFeed class represents the RSS feed from a high level perspective. The RSSFeed class includes the elements of interest from the channel portion of the RSS data source as well as a list of RSSItems. The RSSItem represents the individual item in an RSS channel. Look at both of these important classes. RSSFeed.java is found in Listing 4.


Listing 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;
    }
}

When the SAX parsing engine is complete with parsing the XML data in your RSS source, an instance of the RSSFeed class has been created and now contains everything required to work with the RSS data in your application. The RSSFeed class contains three important elements, and Set-ers and Get-ers for data manipulation. The data elements of interest are:

  • Title (_title): A Java.lang.String to hold the title of the channel for display.
  • Publication Date (_pubdate): A java.lang.String to hold the publication date for display. Note that in a more sophisticated RSS reader, you can use this publication date information to perform an intelligent update or refresh operation.
  • List of Items (_itemlist): A parameterized java.util.List for holding a collection of RSSItems.

RSSItem

Let's look at the list of items contained in RSSFeed. Each element in that list is of type RSSItem. The RSSItem class defines a member of type java.lang.String to hold the value found by the parser. Having each element in a convenient retrieval class makes the application logic and user interface rendering a much more straight-forward task as you will soon see. RSSItem.java is shown in Listing 5.


Listing 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;
    }
}

An instance of the RSSItem class contains the textual data elements related to an individual item found in the RSS feed. In addition to the java.lang.String fields to hold the data, the class includes a Get-er and Set-er for each field. Lastly, this class overrides the toString() method as this is the method invoked when the Android user interface elements display the list of items. Keep this comment regarding the toString() method in mind as it is required in the discussion of rendering the RSS data in the Android user interface.


The SAX approach

As introduced briefly earlier, the SAX approach to XML parsing relies on a callback structure where the parser scans the data stream to look for tags and invokes methods in a handler (which you supply) to process the data. The handler extends a Java class known as the DefaultHandler. There are five methods of interest in the handler. Each one performs a separate, distinct and important role in the XML parsing process. These methods are:

  • startDocument: Invoked when the document parsing commences. This is an opportunity to initialize required data structures.
  • endDocument: Invoked when document parsing concludes.
  • startElement: Called when a new tag is encountered by the scanner. Handlers typically use this method to determine location in the document to properly be prepared to store the data when ready. Additionally, any attributes associated with this elements are available for processing, storing and interpreting.
  • endElement: Called when a closing tag is encountered by the scanner. Handlers typically use this method to determine location in the document and also to store intermediate data. In the case of the RSS sample application, each RSSItem is stored into the RSSFeed object when it encounters the </item> tag.
  • characters: Called when data from a tag is available. This is the opportunity to store textual data. For example, when parsing RSS data, the contents of the title, description, link, category, and publication date elements are all stored in this method.

RSSHandler

The RSSHandler class is key to the operation of the tutorial application. It is responsible for identifying and storing the relevant information in the XML data stream. Each of its methods is invoked in turn when called upon by the SAX parser.

The RSSHandler implements some very basic state handling to properly identify and store data as the SAX parser makes its way through the XML data stream. There are many right ways to manage state in the SAX approach. Though not necessarily relevant to the topic of RSS feed parsing in this tutorial, if you find that managing state while parsing a particular XML data source becomes too complex with SAX, you might consider switching to the DOM parser instead. A DOM parser works on the assumption that every element of the document is important and that your application will desire to explore and extract various elements in the XML data source. SAX works well for an RSS feed because the relationships are so simple. The implementation of RSSHandler.java is shown as Listing 6. Have a quick look at the methods to become familiar with the structure of the handler.


Listing 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 explained

Next examine the RSSHandler class a little further. Note that the class has a single instance of the RSSFeed class. The purpose of the RSSHandler class is to implement callbacks from the SAX parser, and in the process of doing so, assemble a representation of the RSS data for the application to use.

The startElement method assigns which data element was found while the characters method actually performs the assignment to one of the RSSItem members through the appropriate set method. The endElement checks for the end of the item element and when found adds the current RSSItem to the RSSFeed.

The RSSHandler is designed to be self contained for SAX parsing. The methods all react to the parser's events, building up the RSSFeed and then the class makes the fully populated RSSFeed object available through the getFeed method.


Setting up SAX

Now that you have a feel for what happens when the SAX parser is operating, look at the invocation of the SAX parser. The relevant code is found in the RSSFeed class in the getFeed() method, in Listing 7.


Listing 7. getFeed() method in RSSFeed.java
                    
    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;
        }
    }

Following along with the code in Listing 7, you see that you instantiate both the classes required by the SAX parser and your RSSHandler class. Once you assign our RSSHandler to the XMLReader instance, you can commence with the parse. Remember, the hard work in SAX is defining your handler! But before you can parse data, you have to retrieve it.


Fetching XML data

The HTTP transaction to fetch the XML data stream takes place through the URL class, passing its Stream to a new instance of the InputSource class. The SAX parser/scanner uses the InputSource to navigate the XML data stream and execute the parsing operation by invoking the methods in the assigned handler. In this case, those methods are found in the RSSHandler class. Once the parse is complete, the RSSFeed is retrieved from the RSSHandler which built up an instance of the RSSFeed class during each callback operation along the way.

A parsing operation is attempted within a try/catch block. If the operation is successful, an instance of RSSFeed is returned. If an error occurs, the exception is caught and the function returns null.

With a complete an instance of the RSSFeed class available it is time to render the data to the user.

4 of 9 | Previous | Next

Comments



static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=XML, Open source
ArticleID=295231
TutorialTitle=Build a mobile RSS reader
publish-date=03182008
author1-email=fableson@msiservices.com
author1-email-cc=dwxed@us.ibm.com