Build a mobile RSS reader

Handle XML with Android

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.

Frank Ableson, Software designer

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.



18 March 2008

Before you start

This tutorial introduces XML handling on the Android platform. To build the sample application in this tutorial, the Android SDK must be installed and functional on the development computer. Building Android applications in Eclipse is recommended, but not a requirement for this tutorial. Mobile development experience is helpful, but Java™ programming skills are required for the Android applications and will be helpful for this tutorial.

About this tutorial

Frequently used acronyms

  • DOM: Document Object Model
  • GUI: Graphical user interface
  • HTTP: Hyper Text Transport Protocol
  • RSS: Really Simple Syndication
  • SAX: Simple API for XML
  • SDK: Software Development Kit
  • XML: Extensible Markup Language

Why do you care about an RSS reader for Android? And where does XML fit into the picture? First, Android is a platform that is rapidly gaining mindshare in the mobile marketplace. Even before consumer devices equipped with Android are available, thousands of developers are jumping into this mobile market, hoping to be prepared for the much anticipated introduction of Android-equipped devices in the near future. Rich devices, such as Android, demand content. Sometimes that content is in the form of games or a productivity application. Beyond mobile e-mail, however, the content that drives data usage the most is news and information. This content might look like news stories relevant to a particular financial market or something as ubiquitous as the weather forecast. And who isn't interested in the weather forecast for the upcoming weekend trip to the beach? Getting data to a mobile device in a fashion that is organic and usable becomes the challenge.

Android includes the WebKit.org browser engine, which means that it delivers a top quality browser experience to the mobile user. However, chasing down multiple Web sites for news and information can be tedious in a small screen with limited input devices. Don't misunderstand, the market has come a very long way since the WAP browser and limited text-based information, however the power of mobile information is when it is available at-a-glance: Enter RSS feeds. RSS is an acronym for "Really Simple Syndication." In essence, RSS is a convenient way to distribute information of interest. RSS makes XML data available; the data contains brief abstracts or teasers of the full information source. If interested, the user can dig deeper and get "the rest of the story" as the radio host Paul Harvey might say. This tutorial demonstrates the major aspects of XML data handling in the construction of an Android RSS reader.

This tutorial is organized into the following sections:

  • RSS basics
  • Android RSS reader application architecture
  • Fetching and parsing XML data with SAX
  • Rendering RSS data in Android

Prerequisites

This tutorial requires several technologies that work together. You will need to obtain all of them to perform the steps of this tutorial.

Full source code is available from Downloads. Source code snippets in this tutorial include:

  • AndroidManifest.xml snippet: This file is the application deployment descriptor for Android applications.
  • RSSReader: This class implements the main GUI and includes code for menu handling.
  • RSSFeed: This class contains a parsed RSS Feed, including information about the RSS channel and a List of the included RSSItems.
  • RSSHandler: This class implements the SAX parser handler to enable the parsing of an XML stream (an RSS feed from the Internet) and populates an instance of the RSSFeed.
  • RSSItem: This class represents a single RSS item parsed out of an XML stream.
  • ShowDescription: This is an Activity that displays the abstract of a selected RSSItem and includes linkable text to enable seamless launching of the Android browser to access the link available in the RSSItem. Launching this Activity demonstrates the use of an Intent with an extra Bundle in a pseudo-synchronous call.
  • R.java: This file represents the GUI identifiers used in the application.

Terminology

Building an Android RSS/XML application requires knowledge of XML, RSS and HTTP, as well as aspects of the Android platform. An understanding of the terms below is helpful for you to get the most out of this tutorial. For additional links for more information on each of these topics, see Resources.

  • Android: The flagship product of the Open Handset Alliance. This is an open source operating environment targeted for mobile devices such as cell phones.
  • RSS: Really Simple Syndication is a data format used to publish information in an efficient manner.
  • XML: eXtensible Mark-up Language is a self describing data format.
  • HTTP: Hyper Text Transport Protocol almost exclusively transfers all RSS feeds.
  • Parser: This tool is designed to extract information from one format and make it accessible to other structures. For example, an XML parser extracts data from an XML data source.
  • Emulator: This software tool is representative of another system. Android is not available today on real hardware platforms to consumers; the sample application is exercised on the Android Emulator.

Covering some RSS basics

Before I jump into the details of downloading an RSS feed and process it with an Android-based RSS reader, let's take a quick look at the need for RSS, where RSS is used, and how it is structured.

Why RSS?

The multitude of information dissemination tools ranges from free radio, public and cable television, printed media, and even that moderately disruptive technology called the Internet with its abundance of Web sites and e-mail subscriptions. The problem with this vast array of choices is that it is often difficult to find the information of real interest and value amongst the noise and volume of data. Thankfully, RSS can assist with this challenge.

RSS stands for "Really Simple Syndication". RSS is an XML data format used by publishers of content to distribute information that is categorized and is suitable for both human and machine consumption. RSS feeds are typically processed and presented to users in a friendly human-readable format such as a news reader including the application built in this tutorial. RSS feeds are also consumed by computers to generate subsequent, aggregated information sources. An example of a machine-consumed RSS feed might be a listing of available rental properties that is retrieved periodically and fed into a travel agent's local reservation system.

RSS is typically mentioned when discussing news stories, however the format can be used to organize and publish many types of information beyond news. For example, many Web sites publish an RSS feed of available pod-casts to distribute audio and video content. Handango.com, a popular online merchant of mobile software and content, supplies RSS feeds of new and popular software titles, categorized by mobile platform. For example, there is a feed for Windows® Mobile, Blackberry, Palm and others. Before too long, there will hopefully be a feed for Android applications as well!

The format of RSS is XML data, meaning the data itself contains descriptive elements such that it is self-contained. The XML layout over the past few years has undergone some changes as the industry has normalized a bit. The most recent and generally accepted version is 2.0. RSS 2.0 is a relatively simple XML layout that is readily parsed by computer programs. In this tutorial, the sample application demonstrates the parsing of an RSS feed taken from the Web site of IBM developerWorks.

RSS version 2.0

Enough discussion in the abstract about which version is most popular, let's examine the specification for RSS 2.0. The RSS 2.0 format includes only a few tags. You have the obligatory <?xml> tag, which every XML document requires. The first real tag is the <rss> tag, which includes the version as an attribute. Following the <rss> tag is one or more channels where each channel includes the elements shown in Listing 1.

Listing 1. Channels and elements in RSS 2.0
<channel>
<title/>
<link/>
<description/>
<pubDate />
<language/>
<copyright/>
<image>
    <title />
    <url />
    <link />
</image>
+ <item />
</channel>
</rss>

Each channel may have one or more items. Each item has the structure shown in Listing 2.

Listing 2. Item structure
<item>
<title />
<description />
<link />
<category />
<pubDate />
</item>

Data usage

The consumer of an RSS feed determines how to display the data. Typically an RSS reader displays some title information regarding the available channels, including the publication date (found in the pubDate element). Remember, RSS is about publishing relevant, timely data so the publication date should not be ignored. Figure 1 demonstrates this tutorial's sample application in action displaying an RSS feed. Figure 1 displays the title of the RSS feed, the publication date and displays the items in a List format. Later in this tutorial, I discuss the mechanics of getting the parsed RSS feed into the Android user interface.

Figure 1. Android RSS reader in action
Android RSS reader in action

The elements of the item tag of an RSS feed describe each contained item of the feed. For example, in this tutorial, you are working with an RSS feed that contains a list of popular tutorials from the developerWorks site at IBM. Each entry in the feed includes enough information to give the consumer a feel for what the topic is about and how to learn more. There are three levels of information, each of increasing verbosity and detail. In addition, each entry contains a publication date and category information. The title of the item gives a brief feel for what the topic is, for example: <title><![CDATA[Develop Android applications with Eclipse]]></title>.

This tells you enough about this item to peak your interest if you care to learn about using Eclipse for Android development. If the consumer (that is, the person using the application) wants to learn more, there is more information to provide, namely the description from Listing 3.

Listing 3. The description
      <description><![CDATA[Android is Google's oft-discussed mobile, 
wireless, computer, and communications platform. You can  take advantage of 
the powerful Eclipse environment to build Android applications using the Android  
Eclipse plug-in. This tutorial introduces Android application development with the 
Eclipse  plug-in, otherwise known as Android Development Tools. The tutorial provides 
an introduction to  Android development with a quick introduction to the platform, a 
tour of Android Development Tools, and includes the construction of two  example 
applications. ]]></description>

Note the use of the CDATA XML technique. CDATA is used when the XML feed might contain mark-up elements which can interfere with the containing XML tags. One of the side effects of CDATA is that the data is taken as is, so you might need additional formatting to occur on the text for it to display properly in the target user interface. You'll see an example of this in the sample application, introduced later in this tutorial.

Finally, if consumers are very interested in this topic, a link is provided where they can find even more information on the specific topic:

<link>
<![CDATA[http://www.ibm.com/developerworks/edu/os-dw-os-eclipse-android.html?ca=drs-]]>
</link>

This tutorial does not dive into any special handling of the category as all of the categories in this particular RSS feed are the same. The publication date is displayed as a best practice technique.

Now that you know a little bit about RSS and where it is used, let's take a high-level look at the RSS reader application architecture.


Android RSS reader application architecture

Building a richly featured RSS reader is a bit of an ambitious project for a brief tutorial, so let's look at what an RSS reader needs to accomplish. Then you'll build out the important elements which are relevant to XML handling and rendering on the Android platform. When you're done, you'll have a functioning RSS reader with some hooks and plans for further extension in the future.

The major requirements of an RSS reader application

The following sections detail how the Android RSS reader addresses the major requirements of an RSS reader application.

Specify the RSS feed of interest

RSS feeds are available from more Internet sites than you can count. The application needs to specify which RSS feed to work with. A fully featured RSS reader includes one or more means of selecting the desired RSS feed. This can include the ability to choose from a number of sites and channels, or allow the user to enter the feed of interest manually in an EditView. To minimize the non-XML related code in this tutorial, the RSS feed URL is simply hard coded into the source. A menu is implemented as a hook to add RSS feed selections, as desired.

Obtain the RSS feed of interest

Before you can do any fancy parsing and data manipulation of an RSS feed, you must retrieve it from the Internet. This means that you connect to the site hosting the RSS feed through an Internet connection (cellular or WiFi) and perform an HTTP GET operation to retrieve the RSS data. The data which comes back is not a file, it is a stream of XML data. The URL class is employed to fetch the data.

Parse the XML data stream

You can parse XML data with multiple mechanisms. All of them involve the navigation of the data stream and delineation of one data element from another with the opportunity to store the data. Different kinds of XML Parsers are available in the Android SDK, in addition to the option to create your own. The Android SDK includes provisions for the two most popular approaches, namely the DOM Parser, as well as the SAX Parser. The DOM approach is well suited for complex XML documents as it builds a node-oriented representation of the XML data in memory. The SAX approach uses callbacks whenever new tags are encountered, allowing the application to store only the data it is interested in. Due to the simplistic nature of the RSS XML structure, the SAX Parser is employed in this tutorial. The tutorial has a class named RSSHandler which implements the SAX Parser callback functions.

Store the RSS data

The RSS feed extracted from the XML data stream must be put into a useful form. The tutorial has two helper classes: RSSFeed and RSSItem, which store the parsed form of the RSS XML data stream in memory. Once the XML data stream is fully parsed, the application interacts with these classes to render the information.

Rendering the RSS feed

The tutorial's sample application employs two Activity classes to provide the user interface. The primary screen lists the RSS feed title and publication date followed by a list of the RSS items. Once an item is selected through a tap, or enter in the Android Emulator, the ShowDescription Activity displays the full detail including the Title, Publication Date, Description and Link elements of the RSS item. The user interface is set up so any links such as e-mail or Web contained in the text are active—if you select them, the appropriate action takes place. For example, if you select the link text, the Android browser launches with the link as the target. In this way, the RSS reader allows all three levels of information to be accessed very intuitively. This is the power of RSS in general, and more specifically the efficacy of RSS on a mobile platform.

Data refreshing and off-line viewing

Refreshing data on a periodic basis is an important aspect to an RSS reader, as is the ability to access the information when off-line, such as when you fly on an airplane and your Android device is in flight mode. Some provision has been made for this in the sample application in the form of a menu hook for refreshing the data, but data persistence and scheduling are beyond the scope of this tutorial.

Next you'll build an application to obtain and display an RSS feed in an Android application.


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.


Rendering RSS data in Android

The XML data from the RSS feed is now safely stored in memory in an instance of RSSFeed, which in turn contains a number of RSSItems in a convenient List structure. The purposes of an RSS reader are to manage the data and display it in an orderly fashion to the user. Here is where the specifics of Android user interface coding and resources come into play. In this section, you will learn about the Android user interface implementation and the way the RSS data is displayed.

Primary user interface

The RSSReader application's startup Activity is the class RSSReader. The entry point of an Activity is the onCreate method. This method is responsible for boot-strapping the user interface and in some cases, such as this one, the method also initiates subsequent operations beyond creating the user interface. Looking at the onCreate method found in RSSReader.java, and showing in Listing 8, you can see that the structure of the application is very simple.

Listing 8. The RSSReader's onCreate method
    private RSSFeed feed = null;

    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.main);
        
        // go get our feed!
        feed = getFeed(RSSFEEDOFCHOICE);

        // display UI
        UpdateDisplay();
        
    }

The onCreate method performs three functions:

  • Sets up the user interface which is identified by R.layout.main and represents the layout contained in main.xml
  • Sets up an instance of the RSSFeed class by calling the getFeed() method
  • Updates the user interface to reflect the RSS feed contents through the UpdateDisplay() method.

User interface layout

The RSSReader Activity's user interface includes two TextViews and a ListView. The TextViews display the channel title and publication date, while the ListView displays the list of RSSItems in the RSSFeed.

Listing 9 contains the layout for the primary Activity's user interface.

Listing 9. main.xml contains the user interface definition for the RSSReader Activity
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="Android RSSReader"
    android:id="@+id/feedtitle"
    />
<TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text=""
    android:id="@+id/feedpubdate"
    />
<ListView
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:id="@+id/itemlist"
    
    />    
</LinearLayout>

The layout in main.xml has very simple user interface views. Note the presence of the android:id attributes in each of the views. These are necessary because the application dynamically updates the content of each of these views.

Rendering

Look at the UpdateDisplay method in Listing 10 to see how the RSSFeed data connects to the user interface. Recall that an instance of the RSSFeed class now contains the RSS data. The UpdateDisplay method is tasked with taking the RSS data and rendering it through the Android user interface.

Listing 10. UpdateDisplay method connects the data to the user interface
    private void UpdateDisplay()
    {
        TextView feedtitle = (TextView) findViewById(R.id.feedtitle);
        TextView feedpubdate = (TextView) findViewById(R.id.feedpubdate);
        ListView itemlist = (ListView) findViewById(R.id.itemlist);
  
        
        if (feed == null)
        {
            feedtitle.setText("No RSS Feed Available");
            return;
        }
        
        feedtitle.setText(feed.getTitle());
        feedpubdate.setText(feed.getPubDate());

        
ArrayAdapter<RSSItem> adapter = new
    ArrayAdapter<RSSItem>(this,android.R.layout.
    simple_list_item_1,feed.getAllItems());

        itemlist.setAdapter(adapter);
        
        itemlist.setSelection(0);

        itemlist.setOnItemClickListener(this);
        
        
    }

Following along in Listing 10, the UpdateDisplay method starts out by connecting the two TextView objects and a ListView object to the layout through the findViewById() method, passing in the respective identifiers.

If the RSSFeed is null, the method displays a message indicating that RSS feed is available.

The channel title and publication dates are displayed in the respective TextView objects.

List of RSSItems

To connect the list of RSSItems to the ListView, you create an instance of the ArrayAdapter class. This parameterized class is constructed to manage items of type RSSItem. The layout of the list is governed by the built-in resource known as simple_list_item_1, which is essentially a list where each entry supports a single line of text. You also pass in a list of all RSSItem entries in your RSSFeed through the getAllItems() method of the RSSFeed class. If you recall, the RSSItem class overrides the default toString() method to allow the ArrayAdapter to get meaningful representations for displaying in the ListView. Listing 11 shows how the method does some simple formatting to make sure the text fits into the ListView nicely.

Listing 11. Overriding the default toString() method of the RSSItem class
    public String toString()
    {
        // limit how much text you display
        if (_title.length() > 42)
        {
            return _title.substring(0, 42) + "...";
        }
        return _title;
    }

This ArrayAdapter is assigned to the ListView and the first entry is selected by calling the setSelection method with an argument of zero.

Lastly, you want to setup a listener to respond to item selections with the setOnItemClickListener method. The RSSReader class implements the OnItemClickListener interface: public class RSSReader extends Activity implements OnItemClickListener.

The listener functionality is implemented in the onItemClick method as seen in Listing 12.

Listing 12. onItemClick implementation
public void onItemClick(AdapterView parent, View v, int position, long id)
     {
         Log.i(tag,"item clicked! [" + feed.getItem(position).getTitle() + "]");

         Intent itemintent = new Intent(this,ShowDescription.class);
         
         Bundle b = new Bundle();
         b.putString("title", feed.getItem(position).getTitle());
         b.putString("description", feed.getItem(position).getDescription());
         b.putString("link", feed.getItem(position).getLink());
         b.putString("pubdate", feed.getItem(position).getPubDate());
         
         itemintent.putExtra("android.intent.extra.INTENT", b);
         
         startSubActivity(itemintent,0);
     }

When an item in the ListView is selected, the application displays the Description element of the chosen RSSItem. Look at the code in onItemClick and note three things:

  • The creation of an Intent which is necessary to launch another Activity, the ShowDescription Activity which is defined in ShowDescription.java
  • The use of a Bundle to pass data to the invoked activity
  • Starting the ShowDescription activity as a sub activity, which aids in bringing about a synchronous effect to the application

ShowDescription

The ShowDescription Activity provides the next level of detail for the chosen RSSItem, as seen in Figure 2.

Figure 2. Next level of detail for the chosen RSSItem
Next level of detail for the chosen RSSItem

The ShowDescription class is listed in Listing 13. Note the process of un-Bundling the RSSItem data and formatting a string to be used in the user interface.

Listing 13. ShowDescription activity
package com.msi.androidrss;

import android.app.Activity;
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;
import android.content.Intent;
import android.view.*;

public class ShowDescription extends Activity 
{
    public void onCreate(Bundle icicle) 
    {
        super.onCreate(icicle);
        setContentView(R.layout.showdescription);
        
        String theStory = null;
        
        
        Intent startingIntent = getIntent();
        
        if (startingIntent != null)
        {
            Bundle b = startingIntent.getBundleExtra("android.intent.extra.INTENT");
            if (b == null)
            {
                theStory = "bad bundle?";
            }
            else
            {
                theStory = b.getString("title") + "\n\n" + b.getString("pubdate") 
				+ "\n\n" + b.getString("description").replace('\n',' ') 
				+ "\n\nMore information:\n" + b.getString("link");
            }
        }
        else
        {
            theStory = "Information Not Found.";
        
        }
        
        TextView db= (TextView) findViewById(R.id.storybox);
        db.setText(theStory);
        
        Button backbutton = (Button) findViewById(R.id.back);
        
        backbutton.setOnClickListener(new Button.OnClickListener() 
        {
            public void onClick(View v) 
            {
                finish();
            }
        });        
    }
}

Take some care to ensure the Activity does not try to process a null Bundle or display erroneous data. Make sure that the variable theStory always has something valid assigned.

The back button

The code has a simple Button OnClickListener to terminate this Activity. Because this Activity was launched with the startSubActivity() method, this call to finish() results in control returning to the calling Activity, which is RSSReader.

Note that the textual representation of this description includes a hyperlink. Android takes care this of automatically with the attribute android:autoLink="all" as seen in Listing 14.

Listing 14. ShowDescription.xml defines the user interface for the ShowDescription Activity
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:autoLink="all"
    android:text="story goes here ...."
    android:id="@+id/storybox"
    />
<Button
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="Back"
    android:id="@+id/back"
    />    

</LinearLayout>

With the text linking, to launch a Web site through the link of RSSItem is a zero code operation, as seen in Figures 3 and 4.

Figure 3. Web site references in text are automatically hyper-linked
Web site references in text are automatically hyper-linked

Figure 4 shows the launched Web page from the RSSItem link element. This demonstrates the power and efficiency of RSS. You see a topic of interest in the List of items when you select it. You see a concise abstract and if that conveys a satisfactory amount of information, you're done. If your curiosity is further piqued, click on the link and you have even more detailed information.

Figure 4. A launched Web page from the RSSItem link element
A launched Web page from the RSSItem link element

That's it. You have a functioning RSS reader!

Expansion hooks

In the source code, the final item to look at is the RSSReader.java file where the menus are setup. Recall that this tutorial does not address the topic of selecting the RSS feed nor refreshing it other than on startup, However, you'll note some hooks provided to do this in a future tutorial. Listing 15 contains the two methods required to implement menu behavior.

Listing 15. Menu handling methods
   public boolean onCreateOptionsMenu(Menu menu) 
    {
        super.onCreateOptionsMenu(menu);
        
        menu.add(0,0,"Choose RSS Feed");
        menu.add(0,1,"Refresh");
        Log.i(tag,"onCreateOptionsMenu");
        return true;
    }
    
    public boolean onOptionsItemSelected(Menu.Item item){
        switch (item.getId()) {
        case 0:
            
            Log.i(tag,"Set RSS Feed");
            return true;
        case 1:
            Log.i(tag,"Refreshing RSS Feed");
            return true;
        }
        return false;
    }

The onCreateOptionsMenu() is called once during the life cycle of the Activity and allows the creation of menu items. The second argument to the method call is a unique identifier for the menu.

The onOptionsItemSelected method is invoked when the user selects an item. Using the menu item's identifier through the getId() method, it is straight-forward to respond to a specific menu selection.

It is beyond the scope of this tutorial to expand on this functionality; these methods are included as a starting point for enhancements to the Android RSS reader!


Summary

Wrapping up

This tutorial demonstrated how to build an RSS reader application for Android. A discussion of the structure of RSS data feeds established that the SAX parsing approach was ideal for the simple XML structure of RSS version 2.0. The five methods used by the callback handler for processing XML data with SAX permitted the efficient parsing of RSS data. Once the RSS data was parsed and organized, the data was rendered through a custom View. For who are interested, the tutorial even includes some hooks to extend this sample application to be an RSS reader richer in features.


Download

DescriptionNameSize
Tutorial source codex-androidrss.zip60KB

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

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

 


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

All information submitted is secure.

Choose your display name



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.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

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

 


All information submitted is secure.

Dig deeper into XML on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=XML, Open source
ArticleID=295231
ArticleTitle=Build a mobile RSS reader
publish-date=03182008