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.
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
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.
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.
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 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
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
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
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());.
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:
- Make sure the MDS Simulator is running. The MDS simulator allows the BlackBerry simulator to connect to the network, including the Internet.
- Selecting the
F5key will start the BlackBerry simulator. - 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
The application is built! Feel free to run the application and experiment with different RSS feeds.





