by Jonathan Knudsen
August 2002
Download:
[MIDlet suite JAD]
[MIDlet suite JAR]
[Source code ZIP]
API documentation
Legal notices
The PeekAndPick application allows you to browse news headlines and have
story links emailed to your desktop. This article describes the
client application from a developer's perspective. It covers relevant background
information, provides a high-level view of the application, and describes
components that you can use in your own applications.
PeekAndPick is an end-to-end Java application, comprising both a MIDlet
client and a simple servlet. This article presents the client side of
PeekAndPick and discusses the protocol used between client and server.
You may wish to
download the source code
so you can examine the files as you read the article.
I'll also assume you are already familiar with PeekAndPick from a user's
perspective; if you need to find out more, read the
User's Guide.
The PeekAndPick source code is available as a
zipped archive. There are two ways to
build the application: can use the J2ME Wireless Toolkit graphic
interface (KToolbar), or use Ant on the command line. Both methods rely
on tools in the J2ME Wireless Toolkit, which in turn relies on an installation of
Java 2,
Standard Edition (J2SE) SDK. See Wireless
Development Tutorial Part I for details about installing the wireless
toolkit.
Building PeekAndPick
To build PeekAndPick using KToolbar, follow these steps:
- Unzip the source archive into the toolkit's apps directory. Doing
so will create a new project directory, PeekAndPick20,
and upack files into bin, build, lib, res, and
src subdirectories.
- Run KToolbar.
- Click on Open Project... and select PeekAndPick20 from the list.
Click on Open Project.
- Click on Build. The project should build without errors. You can now run
PeekAndPick by clicking on Run.
To build PeekAndPick using Ant, follow these steps:
- Unzip the PeekAndPick source archive in the directory of your choice.
- Edit the build/build.xml script. You may need to adjust the value of the
midp property to point to your J2ME Wireless Toolkit installation.
- Open a command window and change to the build directory of
the source code. Optionally, type
ant tools to add a task to
Ant. (You need to add the task only once.)
- Type
ant. The project should build without errors. If you
wish to use the Ant task from the last step, type ant jad instead.
This is equivalent to ant but also adjusts the
MIDlet-Jar-Size in the descriptor.
- To run the project in the emulator, type
ant run.
If you wish to
use the ProGuard
obfuscator, you need to download and install it separately.
Make sure the proguard property in build.xml points to the
ProGuard classes, and change the depends attribute of the preverify
target from obfuscate_null to obfuscate_proguard.
Similarly, if you wish to use the Retroguard obfuscator, make sure the
retroguard and midp_lib_empty properties are
properly defined. Change the depends attribute of the preverify
target from obfuscate_null to obfuscate_retroguard.
(For more
information on Retroguard, see
Obfuscating a J2ME MIDlet suite.)
The build.xml script provides several other useful targets:
-
The clean target removes subdirectories of the build directory
that are created during the build process.
-
The reallyclean target does what clean does and also removes the
compiled project and other files.
-
The docs target uses
javadoc to generate API documentation
for the PeekAndPick classes. You can also browse this documentation
online.
RSS Crash Course
To understand how PeekAndPick works, you'll need to understand something about
RSS feeds. RSS (for Rich Site Summary) is an XML
application. An RSS file (a feed) contains a description of the
contents of a Web site. The Sun Wireless Developer site has an RSS feed located at
http://wireless.java.sun.com/xml/wd.rss. (Depending on your browser and
configuration, you may or may not be able to view this file easily.) This site's
RSS feed lists the most recently added items on the site. For each item, the RSS
feed contains a title, a URL, and a short description.
The power of this simple XML file is that any client application that recognizes
RSS can now read a summary of this site. RSS clients are available for
many different platforms. Such clients can easily pull news summaries from
various feeds around the Internet and present them in a variety of ways to the
user.
The Meerkat
service, hosted by the O'Reilly Network, takes the idea of RSS one
step further. Meerkat aggregates items from many different feeds into
a single one, also in RSS format. Meerkat has a
documented API which makes it an extremely powerful
and flexible tool for extending other applications. You can ask for specialized
results, for example
retrieval of items added in the last ten days
that come from feeds
concerning wireless technology. You can perform keyword searches. You can
retrieve results in a variety of formats, not just RSS.
RSS is well documented and widely supported. For more information, try these
resources:
PeekAndPick Architecture
PeekAndPick is an RSS client application written in Java for the Mobile
Information Device Profile (MIDP). Its architecture looks like this:
PeekAndPick architecture
|
PeekAndPick makes two different kinds of network connections. First, it
connects to RSS feeds so it can download article information and display it to the
user. Second, the PeekAndPick MIDlet connects to the PeekAndPick server to send email.
PeekAndPick can connect to any RSS feed on the Internet. The current
version includes several feeds that have been hardcoded in the application, and
the FeedEditor MIDlet can be used to edit the list.
PeekAndPick downloads and parses a feed, and displays a list of the item
titles. The user can browse through the titles, view details behind
any of them, and
mark items that look interesting.
The user can then direct PeekAndPick to include links to all marked items
in a message to be sent to the user's email address. The PeekAndPick
MIDlet
makes an HTTP connection to the PeekAndPick servlet running on a Web server.
The servlet connects to an
email server and sends the message.
Internal Architecture
The PeekAndPick client consists of about 20 classes and interfaces:
PeekAndPick UML class diagram excerpt
(Click image to view classes.)
|
The application's center is PeekAndPick, a
MIDlet. PeekAndPick controls all the other classes
in the application. It also responds to the user's input from several different
screens and figures out what to do next. A second MIDlet,
FeedEditor enables the user to edit the list of feeds.
PeekAndPick calls on classes and interfaces in four packages. The
contents of these packages are described below. Later, I'll go into more
detail, especially about code that you might use in your own applications.
-
The default package contains classes that are specific to the PeekAndPick
application. Instances of
Feed represent the
RSS feeds used by PeekAndPick. FeedStore provides persistent
storage for Feeds, and Parameter also supports
Feed.
-
The
rss package is dedicated to parsing RSS documents.
Parser is an abstract class that defines basic behavior. Concrete
subclasses must be able to parse an RSS document
represented by an InputStream. While they parse, they should spit
out events to registered ParserListeners. One concrete subclass,
kXML12Parser, is provided. It uses the kXML 1.2 parser.
-
The
util package contains a generalized Worker class
that is used for threading support. PeekAndPick's performs its network access
in a separate
thread to avoid locking up the user interface. The Worker
class maintains a queue of tasks that will be run in a single separate worker
thread. The tasks are represented by instances of the WorkerTask
interface, which contains only a run() method, just as
Runnable does. When tasks are complete, or when they
throw exceptions,
a registered WorkerListener is notified.
The util package also contains a generalized
Preferences class and an associated GUI; PeekAndPick uses
Preferences to store the user's email address, the
address of the PeekAndPick server, and the user's chosen font size.
-
Finally, the
display package contains generalized UI
components. ListCanvas, ItemCanvas, and
DisplayItem handle display of the list of titles
and of the details of any article selected by the user.
SplashScreen displays
the application's starting image. The ProgressDisplay
interface specifies a simple interface for an object that can
display a progress indicator to the user. In PeekAndPick 2.0,
ListCanvas implements ProgressDisplay to display a
simple animated progress bar while the application is making network
connections. Code in Worker sets the progress indicator running
when a task is pending. Worker clears the progress indicator
when it has no more tasks to run.
Using a Worker Thread
The util.Worker class provides a general approach to the problem of running
tasks in a separate thread. Worker maintains a queue of
WorkerTasks, which are run in the order they're added to the
queue. When one task finishes, Worker starts the next.
If the queue is empty,
Worker patiently waits for a new task to be added.
An application is notified about task completion or exceptions if it
implements the WorkerListener interface.
PeekAndPick uses Worker to execute two tasks. The first,
ParseTask, retrieves and parses RSS files. The
second, MailTask, connects to the PeekAndPick server to
request that it send email.
Using the Worker class is straightforward. PeekAndPick
begins by creating a Worker and registering itself as the listener
object. This code is in PeekAndPick's initialize()
method:
// mWorker is a member variable.
mWorker = new Worker();
mWorker.setWorkerListener(this);
It's easy to use the worker thread in an application of your own; just create
an instance of WorkerTask and
pass it to the Worker. For example, the following code from
PeekAndPick's commandAction() shows how to
create a task for parsing an RSS feed:
// mParser is a member variable, a Parser instance.
// feed contains the currently selected Feed.
ParseTask task = new ParseTask(mParser, feed, null);
mWorker.queue(task);
Creating a task is even easier. All you do is implement the
WorkerTask interface and put the code for the task inside the
run() method. See ParseTask and MailTask
for examples.
Why doesn't Worker just use java.lang.Runnable instead
of the new WorkerTask interface? Worker needs to
catch exceptions thrown by tasks. The run() method of
Runnable throws no exceptions, so it doesn't provide the
functionality that Worker needs.
You can read more about the general problem of running network activity in its
own thread in Networking, User Experience,
and Threads.
Persistent Preferences
One requirement commonly faced by network-client MIDlets is the need to
store user preferences.
PeekAndPick's Preferences class provides a generalized class for
maintaining and storing user preferences.
User preferences are stored as key-and-value pairs, where each key and each
value is
a String--much as in the java.util.Properties
class in J2SE. You can add or replace a value using put(),
or pass a
key to get() to retrieve a value. Preferences also
includes a putIfNull() method you can use to initialize
missing values. This excerpt from PeekAndPick's
initialize() method shows how the Preferences object
is created. If the method can't load the preferences
(because the application is being run for the first time), it initializes values
from system properties.
// mPreferences is a member variable.
mPreferences = new Preferences("PeekAndPick.preferences");
String email = getAppProperty("PeekAndPick.preferences.email");
String server = getAppProperty("PeekAndPick.server.URL");
mPreferences.putIfNull("Email address", email);
mPreferences.putIfNull("PeekAndPick Server", server);
|
Preferences store themselves using MIDP's RecordStore. When you
create a Preferences object you pass the name of a record store
to the
constructor. Preferences attempts to load itself from that
record store. To save a Preferences object after making changes,
call the save() method.
Editing Preferences
The UI component PreferencesEditor provides
controls to allow users to modify the values of a Preferences
object. It is a subclass of javax.microedition.lcdui.Form. To
create a PreferencesEditor, pass a Preferences object
and a String array of non-editable keys to the constructor. The
PreferencesEditor will not display user interface controls for
non-editable values.
PeekAndPick maintains an email address, a server URL, and
a font size in its
Preferences object. The use enters the value of
the email address (key is "Email address") and the font
size (key is "Font size"),
but the server URL (key is "PeekAndPick Server")
should not be editable--indeed, the user should never see it.
PeekAndPick's getPreferencesEditor() method
creates the
editor this way:
// mPreferences is a member variable.
String[] noShows = { "PeekAndPick Server" };
mPreferencesEditor = new PreferencesEditor(mPreferences, noShows);
This editor form is shown when you choose the Prefs command.
PreferencesEditor creates one TextField for each
editable value in the Preferences object. For PeekAndPick, the
"Email address" and "Font size" are the only editable values, so the
PreferencesEditor contains a TextField and
a ChoiceGroup:
PreferencesEditor with two fields
|
You can reset an existing PreferencesEditor to the new values of a
Preferences object by calling its initialize() method.
To retrieve the edited values from the form, call the extract()
method. This method returns a Hashtable with keys and values for
each of the fields in the form. Note that the returned Hashtable
does not contain any of the non-editable key-and-value pairs. You can take the
values returned by extract() and pass them to the
set() method in Preferences, which adds to or
overwrites the existing values.
Pluggable Parsing
Parsing RSS is one of the simpler tasks in PeekAndPick. When I designed the parsing
classes I made it easy to swap in different XML parsers with only minimal
code changes in the rest of the application. PeekAndPick parses the XML
using a concrete subclass of the abstract
rss.Parser.
Parser is relatively simple. It contains the plumbing for
maintaining a list of listeners, and protected methods for firing
various events to those listeners. Its one abstract method is
parse(). This method is expected to parse the given RSS byte stream
and notify the listeners appropriately.
rss.kXML12Parser is a concrete subclass that uses the kXML 1.2
parser. It's relatively simple; it just walks through the document, searching
for item tags and pulling out the information it needs.
Fortunately for code maintenance, PeekAndPick doesn't know much
about
kXML12Parser. The only place the name even comes up is at creation
time, in PeekAndPick's initialize() method.
PeekAndPick creates the parser and adds itself as an event
listener.
// mParser is a member variable of type rss.Parser.
mParser = new kXML12Parser();
mParser.addParserListener(this);
Thereafter, PeekAndPick treats the object as a Parser
instance, which means it would be easy to drop in a different implementation.
Suppose kXML 2.0 is released. I could write a kXML20Parser class that uses
the new version
and change the initialization in PeekAndPick to use the new class.
Everything else would stay the same.
Instead of creating the parser subclass explicitly, I could even use
Class.forName() and use a system property to specify the name of
the implementation class, but I feel going that far would be overkill.
For more information about using XML parsers in MIDP and the specific task of
parsing RSS, read Parsing XML in J2ME.
Lists and Details
The class that displays PeekAndPick's list of article titles is
display.ListCanvas. It is not specific to PeekAndPick or RSS. It
can display any list of items, where each item consists of a name, a
description, and another piece of text that is not shown to the user. In
addition, ListCanvas allows the user to check or uncheck each item
in the list.
ListCanvas in action
|
You can add items to ListCanvas by calling addItem().
Item information can be retrieved using the getShortText(),
getLongText(), and getExtra() methods, each of which
takes an index to the item. Alternately, you can retrieve a complete item with
getItem(), which returns an instance of DisplayItem.
ListCanvas is a subclass of Canvas and handles key
events. Specifically, a press on the right-arrow key of the device will cause
ListCanvas to display details for a single item, using
ItemCanvas. You can also request this behavior by calling the
details() method. Similarly, ItemCanvas will respond
to a left-arrow key press by returning to the original ListCanvas.
The setMarkingAllowed() method in ListCanvas controls
whether or not marking (checking) is supported. If it is,
each time the select key is pressed,
ListCanvas toggles an item's check mark.
You can find out whether an item is marked by calling the
isMarked() method.
Functionally, this class is similar to the
MULTIPLE type of javax.microedition.lcdui.List.
I created ListCanvas to allow more sophisticated navigation instead
of
relying on Commands exclusively. Note that, like List,
ListCanvas adjusts itself to the available screen size, so it looks
right on a mobile phone as well as a PDA.
A Simple Splash Screen
The display.SplashScreen class is a simple implementation of a
splash screen that displays an image. When you instantiate it, pass the constructor
the name of the image
file (a Display object) and the next screen that should appear
when the splash screen goes away.
The splash screen dismisses itself after a hard-coded time-out (three seconds).
It will also dismiss itself in response to a key press or a pointer press.
Client-Server Protocol
You now understand how PeekAndPick parses RSS feeds. In this section I'll
describe how the PeekAndPick client MIDlet talks to the PeekAndPick server,
and requests that it send email. The protocol
is very simple. The client MIDlet sends an HTTP POST request whose
body is of this form:
log line
destination line
message line 1
message line 2
...
message line n
|
The first line contains information that the servlet can write into the server
log. In PeekAndPick's current implementation this line is blank. The second line
is the destination email address, the one the user entered into the
preferences screen. The remainder of the request body is the message itself.
The MIDlet creates this message from the items in the title list
that the user has marked.
On the server side, the servlet simply parses out the information from the HTTP
POST request and uses the Java Mail API to create a message and send it.
Summary
In this article you learned about the design and implementation of the
PeekAndPick client MIDlet. You encountered many classes you can use in
your own applications. PeekAndPick is a useful application and a demonstration
of MIDP client
programming. It should give you a taste of how an end-to-end Java solution
works. Please feel free to use pieces of PeekAndPick in your own applications,
subject to the license.
About the Author: Jonathan Knudsen
[e-mail]
[home page]
is the author of several books,
including
Wireless Java (second edition),
The Unofficial Guide to LEGO MINDSTORMS Robots,
Learning Java (second edition), and
Java 2D Graphics.
Jonathan has written
extensively about Java and Lego robots,
including articles for JavaWorld, EXE, NZZ Folio,
and the O'Reilly Network.
Jonathan
holds a degree in mechanical engineering from Princeton University.
Back To Top
|