Professional Documents
Culture Documents
February 2015, - Prepared by Eyob Gebretisnae. Center of Information Technology & Scientific Computing
Chapter Three
Persistent Storage
3.1. Introduction
Real world applications produce data that needs to be saved, or persisted. This chapter teaches
you how to use the MIDP persistence API. Every MIDP application will need to store data. The
original version of the MIDP did not offer access to the file system, but it did introduce a
complementary concept: the record store.
In this chapter, you’ll learn about the record store as it’s provided through the
javax.microedition.rms interface. When you finish this chapter you will understand how the
record store represents records; how to create, remove, and share record stores; and how to
access, mutate, and remove records.
3.2. Managing Record Stores
The RecordStore class serves two purposes. First, it defines an API for manipulating individual
records. Second, it defines an API (mostly static methods) for managing record stores. To open
a record store, you simply need to name it.
public static RecordStore openRecordStore(String
recordStoreName, boolean createIfNecessary) throws
RecordStoreException, ecordStoreFullException,
RecordStoreNotFoundException
If the record store does not exist, the createIfNecessary parameter determines whether a new
record store will be created or not. If the record store does not exist, and the createIfNecessary
parameter is false, then a RecordStoreNotFoundException will be thrown. The following code
opens a record store named “Address”:
RecordStore rs = RecordStore.openRecordStore("Address", true);
// the record store will be created if it does not already
exist.
An open record store can be closed by calling the closeRecordStore() method. As with anything
that can be opened and closed, it’s a good idea to close record stores when you’re finished with
them. Memory and processing power are in short supply on a small device, so you should
remember to clean up after yourself as much as possible. You probably shouldn’t even keep a
Mobile Programming Handout 1 Addis Ababa University, Institute of Technology,
February 2015, - Prepared by Eyob Gebretisnae. Center of Information Technology & Scientific Computing
record store open over the lifetime of the MIDlet; after all, your MIDlet may be paused by the
device’s application manager, and it would be unwise to have open resources while the MIDlet is
paused. To find out all the record stores available to a particular MIDlet suite, call the
listRecordStores() method:
public static String[] listRecordStores()
Finally, to remove a record store, call the static deleteRecordStore() method. The record store
and its contained records will be deleted.
Adding a record
To add a new record, supply the byte array to the addRecord() method:
public int addRecord(byte[] data, int offset, int numBytes)
throws RecordStoreNotOpenException, RecordStoreException,
RecordStoreFullException
The added record will be numBytes long, taken from the data array starting at offset. The new
record’s ID is returned. Most other record operations need this ID to identify a particular record.
There’s no explicit maximum record length, although, of course, there will be limits based on
the amount of space that is available on the device for record stores.
The following code fragment illustrates adding a new record to a record store named rs.
It creates a byte array from a String, and then writes the entire byte array into a new record.
String record = "This is a record";
byte[] data = record.getBytes();
int id = rs.addRecord(data, 0, data.length);
Retrieving Records
You can retrieve a record by supplying the record ID to the following method:
public byte[] getRecord(int recordId)
throws RecordStoreNotOpenException,
InvalidRecordIDException,
RecordStoreException
This method returns a freshly created byte array containing the record with the requested
ID. An alternate version of this method puts the record data into an array that you supply:
public int getRecord(int recordId, byte[] buffer, int offset)
throws RecordStoreNotOpenException, InvalidRecordIDException,
RecordStoreException
This method returns the number of bytes that were copied into your array. If the array you
supply is not large enough to hold the record, an ArrayOutOfBoundsException will be thrown.
You can find out the size of a particular record ahead of time by calling getRecordSize(). Given a
RecordStore rs and a record ID id, here is one way to retrieve a record’s data:
byte[] retrieved = new byte[rs.getRecordSize(id)];
Mobile Programming Handout 1 Addis Ababa University, Institute of Technology,
February 2015, - Prepared by Eyob Gebretisnae. Center of Information Technology & Scientific Computing
Record stores maintain both a version number and a timestamp. The version number is updated
every time the record store is modified. It is represented by an integer and can be retrieved by
calling getVersion(). The record store also remembers the last time it was modified. This
moment in time is represented by a long, which can be retrieved with getLastModified().
Mobile Programming Handout 1 Addis Ababa University, Institute of Technology,
February 2015, - Prepared by Eyob Gebretisnae. Center of Information Technology & Scientific Computing
RecordStores support a JavaBeans-style listener mechanism. Interested objects can listen for
changes to a record store by registering themselves as listeners. The listener interface is
javax.microedition.rms.RecordListener. You can manage a RecordStore’s listeners with the
following two methods:
public void addRecordListener(RecordListener listener)
public void removeRecordListener(RecordListener listener)
This single method in RecordStore involves three different interfaces. The enumerateRecords()
method returns a sorted subset of the records in a RecordStore. The RecordFilter determines
which records will be included in the subset, while the RecordComparator is used to sort them.
The returned RecordEnumeration allows you to navigate through the returned records.
RecordFilter
The job of a RecordComparator implementation is to determine the order of two sets of record
data. RecordComparator is similar to the java.util.Comparator interface in Java SE. To
implement the RecordComparator interface, you just need to define one method:
public int compare(byte[] rec1, byte[] rec2)
This method examines the data contained in rec1 and rec2 and determines which of them should
come first in a sorted list. It should return one of the following constants defined in
RecordComparator:
• PRECEDES indicates that rec1 should come before rec2.
• FOLLOWS indicates that rec1 should come after rec2.
• EQUIVALENT signals that rec1 and rec2 are the same, at least as far as sorting is concerned.
The following simple implementation compares each byte of the given records and sorts them
numerically. If the two records have the same data, up to the length of the shorter one, then they
are deemed EQUIVALENT.
public class SimpleComparator implements RecordComparator {
public int compare(byte[] rec1, byte[] rec2) {
int limit = Math.min(rec1.length, rec2.length);
for (int index = 0; index < limit; index++) {
if (rec1[index] < rec2[index])
return PRECEDES;
else if (rec1[index] > rec2[index])
return FOLLOWS;
}
return EQUIVALENT; } }
Mobile Programming Handout 1 Addis Ababa University, Institute of Technology,
February 2015, - Prepared by Eyob Gebretisnae. Center of Information Technology & Scientific Computing
RecordEnumeration
Finally, RecordEnumeration offers the possibility of keeping its data synchronized with the
actual RecordStore. Behind the scenes, this can be accomplished by registering the
RecordEnumeration as a listener for RecordStore changes. The basic operation of
RecordEnumeration is to iterate through a set of records. You can find out if there’s a next
record by calling hasNextElement(). If the next record exists, you can retrieve its data by calling
the following method:
public byte[] nextRecord() throws InvalidRecordIDException,
RecordStoreNotOpenException, RecordStoreException
Alternately, you can retrieve the next record’s ID by calling this method:
public int nextRecordId() throws InvalidRecordIDException
If you want to retrieve both the ID and the data for the next record, you’d need to call
nextRecordId() and then retrieve the record data directly from the RecordStore. A typical use of
RecordEnumeration would be to walk straight through the selected records, like this:
// Open a RecordStore rs, Create a RecordFilter rf
// Create a RecordComparator rc
RecordEnumeration re = rs.enumerateRecords(rf, rc, false);
while (re.hasNextElement()) {
byte[] recordBytes = re.nextRecord();
// Process the retrieved bytes.
}
The RecordFilter and RecordComparator can both be null, in which case the
RecordEnumeration will iterate through every record in the record store.
RecordEnumeration makes no guarantees about the order of the returned records if the
Mobile Programming Handout 1 Addis Ababa University, Institute of Technology,
February 2015, - Prepared by Eyob Gebretisnae. Center of Information Technology & Scientific Computing
RecordComparator is null.
As you’re moving through the selected records, you can also move backward.
RecordEnumeration includes hasPreviousElement(), previousRecord(), and previousRecordId()
methods that work just like their next counterparts. Four out of the five ways to move the current
position in the RecordEnumeration are the nextRecord(), nextRecordId(), previousRecord(), and
previousRecordId() methods. The fifth method is kind of like a rewind button: reset() moves the
record pointer back to the very beginning of the selected records. When you’re finished using a
RecordEnumeration, you should release its resources. You can do this by calling destroy(), after
which the RecordEnumeration is no longer usable.
Keeping a RecordEnumeration Up-to-Date
In a multithreaded environment, it’s entirely possible that a RecordStore will change at the same
time you’re iterating through a RecordEnumeration for the same RecordStore. There are two
ways to deal with this. The first thing you can do is call rebuild(), which explicitly rebuilds the
RecordEnumeration based on the RecordFilter and RecordComparator you originally specified.
The other possibility is to request a RecordEnumeration that is automatically updated with any
changes to the underlying RecordStore. You can do this by passing true for the keepUpdated
parameter of RecordStore’s enumerateRecords() method. You can find out if the
RecordEnumeration is automatically updated by calling isKeptUpdated(). Furthermore, you can
change its state by calling keepUpdated().