Skip to main content

Create BlackBerry applications with open source tools, Part 2: Building an RSS reader

After his college basketball career came to an end without a multiyear contract to play for the L.A. Lakers, Frank Ableson shifted his focus to computer software design. He enjoys solving complex problems, particularly in the areas of communications and hardware interfacing. When not working, he can be found spending time with his wife Nikki and their children. You can reach Frank at frank@cfgsolutions.com.

Summary:  In the same way that open source has revolutionized the software development marketplace, the proliferation of alternative news sources has shaken up the traditional news-source monopolies. Today, you can get free news from various Internet sources and from the Internet outlets of the mainstream media. Trekking from one Web site to another to read news stories is possible, but tedious. And what about updates to news stories of interest? Wouldn't it be better if the news of interest is aggregated for your convenience? You can do it with an RSS reader for BlackBerry. Here in Part 2 of this "Create BlackBerry applications with open source tools" series, explore the RSS data-distribution format by creating a BlackBerry RSS reader suitable for taking news wherever you and your BlackBerry go.

View more content in this series

Date:  17 Feb 2009
Level:  Intermediate PDF:  A4 and Letter (262 KB | 31 pages)Get Adobe® Reader®

Activity:  1289 views
Comments:  

Creating the BlackBerry RSS reader

The best way to learn is by doing, so let's get started. This section examines each of the major elements of the tutorial's sample application, including the relevant source-code snippets.

The application structure

You will create a single application piece by piece throughout the tutorial. You can download the complete source code. Figure 2 shows the source files in use in the sample application.


Figure 2. Project file in BlackBerry JDE
Project file in BlackBerry JDE

The first code snippet to review is in IBMRssApplication.java. As in any Java application, an application requires an entry point, such as main in the IBMRssApplication.java file.


Listing 2. Main method of the IBMRssApplication.java file

//
// IBMRssApplication.java
//
// MSI Services, Inc.
// Frank Ableson
// 973.448.0070
// fableson@msiservices.com
// code free to use for any purpose, commercial or otherwise

package com.msi.ibm.rssreader;

// required imports
import net.rim.device.api.ui.*;

// our application 
class IBMRssApplication extends UiApplication
{
    // applicatione entry point
    public static void main(String[] args)
    {
        // create an instance of our app
        IBMRssApplication theApp = new IBMRssApplication();
        // "run" the app
        theApp.enterEventDispatcher();
    }
    // app constructor
    public IBMRssApplication()
    {
        // create an instance of the main screen of our application
        IBMRssScreen screen = new IBMRssScreen();
        // make the screen visible
        UiApplication.getUiApplication().pushScreen(screen); 
    }
}

The main method creates a new instance of the class IBMRssApplication, which is an extension of the UiApplication class. UiApplication is found in the net.rim.device.api.ui package. The UiApplication class is a base class for all BlackBerry applications that have a UI.

The constructor of the IBMRssApplication class creates an instance of the IBMRssScreen class. This class is defined and implemented in IBMRssScreen.java. Once the instance of IBMRssScreen is created, it is passed to the pushScreen() method. This essentially brings the screen into view on the device.

Before exploring the UI of the application, there are some important aspects of the code to consider. The UI relies heavily on functions contained in other classes.


Persistent storage

Data storage and organization are crucial to many applications, and the sample application in this tutorial is no exception. The class IBMRssStorage implemented in IBMRssStorage.java is responsible for data management for the sample application. Data is stored within a RecordStore, which is found in the javax.microedition.rms package. The IBMRssStorage class has a number of helper methods for manipulating the stored records and three contained classes, which represent important constructs for the application.

Data is stored within a RecordStore as a series of random-access, variable-length byte arrays. This tutorial's sample application uses a single RecordStore containing two distinct record types: a header record that represents an RSS feed and a detail record that represents an RSS item. When RSS data is processed by the application, it is segregated into these two record types and persisted. This approach was selected because in a custom application, there may be more data elements added (or subtracted) beyond the base RSS data format used in the distribution of the data.

Storing only the data required makes the application more memory-efficient and gives it faster data access. To conserve space, all records are stored as pipe-delimited strings. The Utils class, implemented in Utils.java, contains the split method, which is used in parts of the code to facilitate manipulation of these records by parsing them and placing the individual data elements into elements of a java.util.Vector instance.

The structure of a Header record is H | Name of Feed | URL to Feed Source | Publication Date. The detail record is defined as D | Name of Feed | Title of Item | Link to Full Story | Description | Publication Date.

Listing 3 shows portions of the IBMRssStorage class responsible for opening and closing the RecordStore and a couple of helper methods for retrieving records.


Listing 3. IBMRssStorage class snippet

class IBMRssStorage 
{
    private RecordStore store;
    IBMRssStorage() 
    {
        try
        {
            store = RecordStore.openRecordStore(Guid.recordStoreName,true);
            store.setMode(RecordStore.AUTHMODE_ANY,true);
        }
        catch (Exception e)
        {
            System.err.println("error in \
            IBMRssStorage \
            constructor [" + e.getMessage() + "]");
        }
    }
    public boolean closeStore()
    {
        try
        {
            store.closeRecordStore();
        }
        catch (Exception e)
        {
            System.err.println("Error closing [" + e.getMessage() + "]");
        }
        return true;
    }
    public int getNumRecords()
    {
        try
        {
            return store.getNumRecords();
        }
        catch (Exception e)
        {
            System.err.println("Error in getNumRecords " + e.getMessage());
            return 0;
        }
    }
    public byte[] getRecord(int recId)
    {
        try
        {
            return store.getRecord(recId);
        }
        catch (Exception e)
        {
            System.err.println("Error in getRecord[" + recId + "] " + e.getMessage());
            return null;
        }
    }

Records can be enumerated according to a specific filter criterion or sort order. To enumerate records in any manner other than in an ordinal fashion, a RecordEnumeration is required. A RecordEnumeration is created by calling the enumerateRecords method of a RecordStore instance. The arguments to this method include a RecordFilter and a RecordComparator, both also part of the javax.microedition.rms package. It is common practice to define an application-specific class that implements both of these interfaces, as shown by the RssFilter class in Listing 4.


Listing 4. RssFilter class

public static class RSSFilter implements RecordFilter, RecordComparator 
    {
        private String _type = "";
        private String _name = "";
        
        RSSFilter(String type,String name)
        {
            _type = type;
            _name = name;
        }
        
        public boolean matches( byte[] recordData )
        {
            try
            {
                String oneRec = new String(recordData);
                Vector v1 = Utils.split(new String(recordData),"|");
                String recordType = (String) v1.elementAt(0);
                String recordName = (String) v1.elementAt(1);
                if (_name != null)   
                {
                    if (recordName.trim().equalsIgnoreCase(_name) &&
 			   recordType.equalsIgnoreCase(_type))
                    {
                        return true;
                    }
                }
                else
                {
                    // just matching type
                    if (recordType.equalsIgnoreCase(_type))
                    {
                        return true;
                    }
                }
            }
            catch (Exception e)
            {
                System.out.println(e);
                e.printStackTrace();
            }
            return false;
        }
        public int compare(byte[] rec1, byte[] rec2)
        {
            int comp = 0;
            try
            {
                String first = new String(rec1);
                String second = new String(rec2);
                Vector v1 = Utils.split(first,"|");
                Vector v2 = Utils.split(second,"|");
                if (_type.equals("H"))
                {
                    //compare name field
                    String r1 = ((String) v1.elementAt(1)).toUpperCase();
                    String r2 = ((String) v2.elementAt(1)).toUpperCase();
                    comp = r1.compareTo(r2);
                }
                else
                {
                    // compare title field
                    String r1 = ((String) v1.elementAt(2)).toUpperCase();
                    String r2 = ((String) v2.elementAt(2)).toUpperCase();
                    comp = r1.compareTo(r2);
                }
            }
            catch (Exception e)
            {                
            }
            if(comp < 0)
            {
                return PRECEDES;
            }
            else if( comp == 0 )
            {
                return EQUIVALENT;
            }
            else
            {
                return FOLLOWS;
            }
        }
     }

The RecordFilter interface is satisfied with the matches method. Each record must be split and reassembled to be properly matched and filtered. Sometimes a RecordFilter is employed to obtain a list of only header records and sometimes to get all records with a particular name field.

The RecordComparator is responsible for sort ordering. It employs a simple string-comparison algorithm to establish an alpha-sort implementation. Note the use of the toUpperCase method to make the sorting case-insensitive.

The IBMRssStorage class contains two additional classes: one each to represent RSS feeds and RSS items. Each class encapsulates parsing and field-level manipulations with appropriate set and get methods. The IBMRssStorage class also includes several helper, or factory, methods to aid in the creation of the IBMRssFeed and IBMRssItem classes based on a variety of input data. The classes and helpers are shown below.


Listing 5. IBMRssFeed and IBMRssItem classes

     public IBMRssFeed createFeed(String name,String url)
     {
         return new IBMRssFeed(name,url);
     }
     public IBMRssFeed createFeed(byte[] recordData)
     {
         return createFeed(new String(recordData));
     }
     public IBMRssFeed createFeed(String recordData)
     {
         Vector v = Utils.split(recordData,"|");
         IBMRssFeed feed = new IBMRssFeed();
         feed.setName((String) v.elementAt(1));
         feed.setUrl((String)v.elementAt(2));
         return feed;
     }
     public class IBMRssFeed
     {
         private String _name;
         private String _url;
         IBMRssFeed()
         {
         }
         IBMRssFeed(String name,String url)
         {
             _name = name;
             _url = url;
         }
         public String getName()
         {
             return _name;
         }
         
         public String getUrl()
         {
             return _url;
         }
          
          
         public void setName(String name)
         {
             _name = name;
         } 
         
         public void setUrl(String url)
         {
             _url = url;
         }
        
         public String toString()
         {
             String ret = "H|";
             ret += _name;
             ret += "|" + _url;
             return ret;
         }
     }
     
     public  IBMRssItem createItem(String name,String title,String link,
	  	String description,String category,String pubdate)
     {
         return new IBMRssItem(name,title,link,description,category,pubdate);
     }
     public IBMRssItem createItem(byte [] recordData)
     {
         return createItem(new String(recordData));
     }
     public  IBMRssItem createItem(String recordData)
     {
         Vector v = Utils.split(recordData,"|");
         IBMRssItem ret = new IBMRssItem();
         ret.setName((String) v.elementAt(1));
         ret.setTitle((String) v.elementAt(2));
         ret.setLink((String) v.elementAt(3));
         ret.setDescription((String) v.elementAt(4));
         ret.setCategory((String) v.elementAt(5));
         ret.setPubDate((String) v.elementAt(6));
         return ret;
     }
     public IBMRssItem createItem()
     {
        return new IBMRssItem();
     }
     public class IBMRssItem
     {
         private String _name = "";
         private String _title = "";
         private String _link = "";
         private String _description = "";
         private String _category = "";
         private String _pubDate = "";
         IBMRssItem()
         {
         }
         IBMRssItem(String name,String title,String link,
		String description,String category,String pubdate)
         {
             _name = name;
             _title = title;
             _link = link;
             _description = description;
             _category = category;
             _pubDate = pubdate;
         }
         public String getName()
         {
             return _name;
         }
         public String getTitle()
         {
             return _title;
         }
         public String getLink()
         {
             return _link;
         }
         public String getDescription()
         {
             return _description;
         }
         public String getCategory()
         {
             return _category;
         }
         public String getPubDate()
         {
             return _pubDate;
         }
         public void setName(String name)
         {
             _name = name;
         }
         public void setTitle(String title)
         {
             _title = title;
         }
         public void setLink(String link)
         {
             _link  = link;
         }
         public void setDescription(String description)
         {
             _description = description;
         }
         public void setCategory(String category)
         {
             _category = category;
         }
         public void setPubDate(String pubdate)
         {
             _pubDate = pubdate;
         }
         public String toString()
         {
             String ret = "D";
             ret += "|" + _name;
             ret += "|" + _title;
             ret += "|" + _link;
             ret += "|" + _description;
             ret += "|" + _category;
             ret += "|" + _pubDate;
             return ret;
         }
     }

The toString methods, which generate a ready-to-store representation of the record, are used. Recall that in the Java programming language, the toString method of any object may be overridden to provide a useful representation of the data. You could have chosen an alternative method name to accomplish this storage preparation step.

Now that you know where and how the data is stored, let's explore how to get it from the Internet.


Communications

The IBMRssComms class is responsible for fetching the RSS feeds from each of their respective sources on the Internet. This class extends the java.lang.Thread class so it can run independent of the UI. The "Next steps" section discusses some of the rationale for this choice, but for now, let's examine the run method.


Listing 6. IBMRssComms — Fetching data

class IBMRssComms extends Thread
{
    IBMRssComms() 
    {
    }
    public void run()
    {
        InputStream inputStream = null;
        HttpConnection httpConnection = null;
        try
        {
            // open storage
            IBMRssStorage rss = new IBMRssStorage();
            
            // grab list of feeds
            RecordEnumeration feedList = rss.getFeedList();
            
            // process each
            while (feedList.hasNextElement())
            {
                IBMRssFeed theFeed = rss.createFeed(new String(feedList.nextRecord()));

                // delete any Items under this feed
                rss.deleteFeed(theFeed.getName(),true);
                
                // connect to feed's URL
                httpConnection = (HttpConnection)Connector.open(theFeed.getUrl());
                inputStream = httpConnection.openDataInputStream();
                
                // good connection?
                if(httpConnection.getResponseCode() == HttpConnection.HTTP_OK)
                {
                    // check header field for a specific encoding
                    String desiredEncoding = "ISO-8859-1";  //iso-8859-1
                    String contenttype = httpConnection.getHeaderField("Content-Type");
                    if (contenttype != null)
                    {
                        contenttype = contenttype.toUpperCase();
                        if (contenttype.indexOf("UTF-8") != -1)
                        {
                            desiredEncoding = "UTF-8";
                        }
                    }

                    // we need an input source for the sax parser
                    InputSource is = new InputSource(inputStream);

                    // setup Encoding to match what the Web server sent us
                    is.setEncoding(desiredEncoding);
                    
                    // create the factory
                    SAXParserFactory factory = SAXParserFactory.newInstance();
        
                    // create a parser
                    SAXParser parser = factory.newSAXParser();
                    
                    // instantiate our handler
                    IBMRssXMLHandler myHandler= new IBMRssXMLHandler(theFeed);

                    // perform the synchronous parse           
                    parser.parse(is,myHandler);
                }
            }
            
            // dump feeds to debug window
            rss.dumpFeeds();
            
            // close storage
            rss.closeStore();
        }
        catch (IOException ioe)
        {
            System.err.println("IO Exception !: " + ioe.getMessage());
            ioe.printStackTrace();
        }
        catch (SAXException saxe)
        {
            System.err.println("SAX Exception !: " + saxe.getMessage());
            saxe.printStackTrace();
        }
        catch (Exception e)
        {
            System.err.println("General Error " + e.getMessage());
            e.printStackTrace();
        }
        // notify gui that we're done!
        ApplicationManager.getApplicationManager().postGlobalEvent(Guid.rssdatadone,0,0); 
    }
}

The IBMRssComms class enumerates over each available RSS feed in the RecordStore (using IBMRssStorage methods) and fetches the XML data associated with it. This data is subsequently parsed by the SAX XML parser, and the data is stored into the RecordStore appropriately. The HttpConnection class is used to obtain the data stream, and its InputStream is used to create an InputSource. This InputSource is used, along with an instance of a tutorial sample application class, to parse the XML data.

The data is parsed with the assistance of the IBMRssXMLHandler class. Interestingly, this is the only class in the entire application that truly knows anything about the underlying RSS data structure as received from the Internet source. Listing 7 contains this class, which extends the DefaultHandler class from the org.xml.sax package.


Listing 7. IBMRssXMLHandler.java class

package com.msi.ibm.rssreader;
import org.xml.sax.helpers.*;
import org.xml.sax.*;
import java.lang.StringBuffer;
import com.msi.ibm.rssreader.IBMRssStorage.*;

class IBMRssXMLHandler extends DefaultHandler
{
    StringBuffer sb = null;
    IBMRssFeed _feed = null;
    IBMRssItem item = null;
    boolean bStarted = false;
    IBMRssStorage rssStore = null;
    IBMRssXMLHandler(IBMRssFeed feed) 
    {
        _feed = feed;
        rssStore = new IBMRssStorage();
    }
    public void warning(SAXParseException e) 
    {
        System.err.println("warning: " + e.getMessage());
        bStarted = false;
    }
    public void error(SAXParseException e) 
    {
        System.err.println("error: " + e.getMessage());
    }
    public void fatalError(SAXParseException e) 
    {
        System.err.println("fatalError: " + e.getMessage());
        bStarted = false;
    }
    public void startDocument() throws SAXException
    {
    }
    public void endDocument() throws SAXException
    {
        rssStore.closeStore();
    }
    public void startElement(String namespaceURI, String localName,
		String qName, Attributes atts) throws SAXException
    {
        sb = new StringBuffer("");
        if (localName.equals("item"))
        {
            bStarted = true;
            // new item, let's set up!
            item = rssStore.createItem();
        }
    }
    public void endElement(String namespaceURI, String localName,
		String qName) throws SAXException
    {
        if (bStarted == false) return;
        if (localName.equals("item"))
        {
            item.setName(_feed.getName());
            rssStore.addRecord(item); 
        }
        if (localName.equals("title"))
        {
            item.setTitle(sb.toString());
        }
        if (localName.equals("link"))
        {
            item.setLink(sb.toString());
        }
        if (localName.equals("description"))
        {
            item.setDescription(sb.toString());
        	    }
        if (localName.equals("category"))
        {
            item.setCategory(sb.toString());
        }
        if (localName.equals("pubDate"))
        {
            item.setPubDate(sb.toString());
        }                        
        sb = new StringBuffer("");
    }
    public void characters(char ch[], int start, int length)
    {
        String theString = new String(ch,start,length);
        sb.append(theString);
    }
}

Methods in the IBMRssXMLHandler class are invoked by the SAX parsing engine as certain events occur and various tags are encountered. For example, when a startElement is encountered, a new IBMRssItem is initialized. As each element of the item is encountered, data is stored. Subsequently, when the </item> field is encountered, the class knows that a complete IBMRssItem is ready to be stored.

When the complete cycle of enumerating over each feed, fetching the underlying data source, parsing the data, and storing this data is complete, the last line of the IBMRssComms class' run method posts a global event indicating that the data update process is complete: ApplicationManager.getApplicationManager().postGlobalEvent(Guid.rssdatadone,0,0);.

The value rssdatadone is defined in the Guid class and implemented as a static final member in Guid.java. When this event is caught, as seen in the next section, the UI is updated.

All of the data is fetched, parsed, and stored, so let's return to examining the UI.


The screen

The example application has a very basic UI. The IBMRssScreen class extends the MainScreen class, which is a class provided by RIM that implements features common to BlackBerry applications. IBMRssScreen also implements the Java interface's ListFieldCallback and GlobalEventListener.The ListFieldCallback interface lets the class react to requests by the UI to draw items in a ListField control. The GlobalEventListener interface allows the UI to update after a new RSS feed has been retrieved from the Internet. Figure 3 shows the application screen when first loaded, including a few preloaded RSS feed entries.


Figure 3. IBMRssScreen showing some available RSS feeds
IBMRssScreen showing some available RSS feeds

Listing 8 contains the UI definitions and initialization code.


Listing 8. Private definitions of IBMRssScreen and the constructor

class IBMRssScreen extends MainScreen implements  ListFieldCallback, GlobalEventListener
{
    // private members - these represent the "controls"
    private LabelField statusField = null;
    private ListField feedList = null;
    private RecordEnumeration feeds = null;
    private int[] feedIds = null;
    private int[] itemIds = null;
    private int mode = 0;               // 0 is feeds, 1 is items
    private IBMRssStorage rss  = null;
    private MenuItem mnuRefreshFeeds = new MenuItem("Refresh Feeds", 100, 10) 
    {
        public void run() 
        {
            try
            {
                statusField.setText("Refreshing Feeds, Please Wait");
                feedList.setSize(0);
                IBMRssComms comms = new IBMRssComms();
                comms.start();
            }
            catch (Exception e)
            {
                System.err.println("Error Refresh Menu: " + e.getMessage());
                e.printStackTrace(); 
            }
        }
    };
    private MenuItem mnuSelectItem = new MenuItem("Select Item", 100, 10) 
    {
        public void run() 
        {
            try
            { 
                if (mode == 0)
                {
                    if (feedList.getSelectedIndex() >= 0)
                    {
                        loadFeed(feedList.getSelectedIndex());
                    }
                }
                else if (mode == 1)
                {
                    if (feedList.getSelectedIndex() >= 0)
                    {
                        showItem(feedList.getSelectedIndex());
                    }
                }
            }
            catch (Exception e)
            {
                System.err.println("Error Select Item Menu: " + e.getMessage());
                e.printStackTrace(); 
            }
        }
    };
    // constructor
    public IBMRssScreen()
    {
        // invoke the constructor of the super class (MainScreen)
        super();
        
        // give our application window a title
        setTitle("IBM Rss App");

        // setup our storage system
        rss = new IBMRssStorage();
        
        if (rss.getNumRecords() == 0)
        {
           
            IBMRssFeed myFeedDevSource = rss.createFeed("DevSource",
		 "http://feeds.ziffdavisenterprise.com/RSS/devsource.xml");
             rss.addRecord(myFeedDevSource);
            
            IBMRssFeed myFeedIBM = rss.createFeed("Developerworks",
		 "http://www.ibm.com/developerworks/views/opensource/
		 rss/libraryview.jsp");
             rss.addRecord(myFeedIBM);
            
            IBMRssFeed myFeedNY = rss.createFeed("New Yorker",
		 "http://xml.newsisfree.com/feeds/76/13276.xml");
             rss.addRecord(myFeedNY);
            
            IBMRssFeed myFeedAIG = rss.createFeed("Answers In Genesis",
		 "http://www.answersingenesis.org/store/rss/newest");
             rss.addRecord(myFeedAIG);
            
            IBMRssFeed myFeed = rss.createFeed("Handango BB Apps",
		 "http://service.handango.com/ampp/ContentRequestGenerator?
		 id=123&password=rss20content&platformId=5
		 &maxCount=50&optionId=1");
             rss.addRecord(myFeed);
        }

        // display the fields in debug window
        //rss.dumpFeeds();
        
        // create user interface components
        createui();

        // load the data
        setupdata();

        // add listeners
        addKeyListener(new RssKeyListener());
        UiApplication.getUiApplication().addGlobalEventListener(this);
    }

The two menus required by the application are defined as private class-level members of type MenuItem. After invoking the super() method to initialize the superclass and setting up a title, the persistent storage layer is set up with an instance of the IBMRssStorage class.

A few sample feeds are added if there are none found in the RecordStore. The commented-line rss.dumpFeeds() is a helper method to display the storage contents to the JDE output window. A call to the createui method adds the UI elements to the screen, and the setupdata method causes the data to be populated to the UI.


Listing 9. createui and setupdata methods

    private void createui()
    {
        try
        {
            addMenuItem(mnuRefreshFeeds);
            addMenuItem(mnuSelectItem);
              
            statusField = new LabelField("Select a Feed Below");
            add(statusField);
            add(new SeparatorField());
            feedList= new ListField();
            feedList.setCallback(this);
            add(feedList);
            
        }
        catch (Exception e)
        {
            System.out.println("Failed to create user interface components");
        }
    }

    private void setupdata()
    {
        mode = 0;
        feeds = rss.getFeedList();
        feedIds = new int[feeds.numRecords()];
        int i = 0;
        try
        {
            while (feeds.hasNextElement())
            {
                feedIds[i++] = feeds.nextRecordId();
            }
        }
        catch (Exception e)
        {
            System.err.println("Error enumerating Feeds " + e.getMessage());
        }
        statusField.setText("Select a Feed Below");
        feedList.setSize(feeds.numRecords());
        feedList.invalidate();
    }

Then the constructor adds a KeyListener and a GlobalEventListener to this screen. The KeyListener is implemented by RssKeyListener while the GlobalEventListener interface is satisfied by the IBMRssScreen class itself.

The menus are added to the screen with the addMenuItem method. The other controls are added to the MainScreen with the add method. The MainScreen implements a single VerticalManager so each added field or control simply stack beneath one another vertically.

When selected, mnuRefreshFeeds clears the ListField by setting its size to 0, then creates an instance of IBMRssComms, which is then started. While the IBMRssComms thread is executing, the UI tells the user that something is happening and to sit tight.


Figure 4. Application refreshing RSS data feeds
Application refreshing RSS data feeds

When all of the RSS feeds have been refreshed, an event is posted that's caught by IBMRssScreen's eventOccurred method. Remember, this class implements the GlobalEventListener interface.


Listing 10. GlobalEventListener interface implementation

public void eventOccurred( long guid, int data0, \
int data1, Object object0, Object object1) 
{
   if (guid == Guid.rssdatadone)
   {
        setupdata();
   }
}

Now that you have all of the data refreshed, examine how it's actually displayed on the screen in the ListField. Listing 11 shows two of the methods required by the ListFieldCallback interface.


Listing 11. ListFieldCallback interface implementation

    public void drawListRow(ListField listField,Graphics \
    graphics,int index,int y,int width)
    {
        graphics.setFont(Font.getDefault());
        if (mode == 0)
        {
            IBMRssFeed thisFeed = rss.createFeed(rss.getRecord(feedIds[index]));
            graphics.drawText(thisFeed.getName(),2,y,DrawStyle.TOP,width);
        }
        else
        {
            IBMRssItem thisItem = rss.createItem(rss.getRecord(itemIds[index]));
            graphics.drawText(thisItem.getTitle(),2,y,DrawStyle.TOP,width);
        }
    }

    public int getPreferredWidth(ListField listField)    
    {
        return Graphics.getScreenWidth();
    }

The getPreferredWidth method is fairly straightforward; it is only concerned with how wide the list should be drawn. The real work takes place in the drawListRow method, which uses the arguments passed to determine which data to supply. In the sample application, the private member variable named mode determines whether the ListField is displaying the feeds or the items for a particular feed. You can see this clearly in drawListRow as the method retrieves the appropriate data element and uses the graphics instance to draw the text with the drawText method.

There are two arrays of integers used to cache the record IDs for quick access to a particular record. The arrays are updated each time a new RSS feed is selected. Code to manage the feedIds is found in the setupdata method. The code to manage the itemIds is in the loadFeed method.


Listing 12. loadFeed method caches record IDs

   private void loadFeed(int feedIndex)
    {
        IBMRssFeed thisFeed = rss.createFeed(rss.getRecord(feedIds[feedIndex]));

        statusField.setText(thisFeed.getName());

        RecordEnumeration items = rss.getFeedItems(thisFeed.getName());
        itemIds = new int[items.numRecords()];
        int i = 0;
        try
        {
            while (items.hasNextElement())
            {
                itemIds[i++] = items.nextRecordId();
            }
        }
        catch (Exception e)
        {
            System.err.println("Error enumerating items in feed [" +
		 thisFeed.getName() + "] " + e.getMessage());
        }
        mode = 1;
        if (items.numRecords() > 0)
        {
            feedList.setSelectedIndex(0);
        }
        feedList.setSize(items.numRecords());
        feedList.invalidate();
    }

When an item is selected, the showItem method is invoked, which creates an instance of IBMRssDescription, which is a contained class of IBMRssScreen. Figure 5 shows this screen in action.


Figure 5. Selecting an item from a feed
Selecting an item from a feed

When Full Story is selected, the item's link is opened in the BlackBerry browser with a single line of code: Browser.getDefaultSession().displayPage(_item.getLink());.


Running the application

At this point, you've reviewed all of the important code snippets, and it's time to build and test the application. If you're new to BlackBerry development and need some help building an application in the JDE, see Resources.

Assuming the application has been built without errors, you can run the application in the BlackBerry simulator:

  1. Make sure the MDS Simulator is running. The MDS simulator allows the BlackBerry simulator to connect to the network, including the Internet.
  2. Selecting the F5 key will start the BlackBerry simulator.
  3. The tutorial sample application will not start right away. To start it, navigate to the application's icon on the home-page application ribbon and select the tutorial sample application, named IBMRssReader, with the RSS icon as shown below. The arrow keys on the computer simulate the track wheel, the Enter key simulates pressing the track wheel, and the Esc key emulates the BlackBerry Esc button.


Figure 6. IBMRssReader on the BlackBerry simulator
IBMRssReader on the BlackBerry simulator

The application is built! Feel free to run the application and experiment with different RSS feeds.

3 of 8 | Previous | Next

Comments



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source
ArticleID=376403
TutorialTitle=Create BlackBerry applications with open source tools, Part 2: Building an RSS reader
publish-date=02172009
author1-email=
author1-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Special offers