You are on page 1of 13

J.E.D.I.

The Record Management System


MIDP provides an API where programs can persist application data locally on the device. The MIDP Record Management System is a facility for MIDlets to store application data across MIDlet invocations. Data is stored into the non-volatile memory of the device. This means that stored program data will not be lost even if the program is restarted or the device is turned off. The Record Management System is a record-oriented database system for MIDP devices. A record in this database is simply an array of bytes with an index.

1 Objectives
At the end of the lesson, the student should be able to:

Understand the concept of a Record Store Create and Open Record Stores Add, Retrieve, Update and Delete records Enumerate records using a RecordEnumerator Create a Record Comparator Create a Record Filter

2 The Record Store


A Record Store is a collection of records. Record Ids in a Record Store are unique. Record Ids are automatically allocated at record creation time and act as the index or primary key. Record Ids are assigned sequentially, and the first Record ID assigned to the first record of every Record Store is 1 (one). When a record is deleted, its record id will not be reused. If we create four records and delete the fourth record, the next record Id that the system will assign is 5 (see Figure). Record ID 1 2 3 5 Array of bytes Data of record #1 Data of record #2 Data of record #3 Data of record #5
1

Mobile Application Development

J.E.D.I.

MIDlets can create more than one record store. The record store name should be unique within the MIDlet suite. Record store names are case sensitive. The maximum length of a Record Store is 32 characters. When a MIDlet suite is deleted from a device, all record stores associated with the MIDlets in this suite will also be deleted.

Mobile Application Development

J.E.D.I.

Creating and Opening Record Stores The following method creates or opens Record Stores: static RecordStore openRecordStore(String recordStoreName, boolean createIfNecessary) static RecordStore openRecordStore(String recordStoreName, boolean createIfNecessary, int authmode, boolean writable) static RecordStore openRecordStore(String recordStoreName, String vendorName, String suiteName) If createIfNecessary is set to true and the Record Store does not yet exist, the Record Store will be created. If createIfNecessary is set to false and the Record Store is nonexistent, a RecordStoreNotFoundException will be thrown. The authmode parameter may be RecordStore.AUTHMODE_PRIVATE or RecordStore.AUTHMODE_ANY. Using AUTHMODE_PRIVATE will cause the Record Store to be accessible only by the MIDlet suite of the owner MIDlet. Setting authmode to AUTHMODE_ANY will make this Record Store to be accessible by ANY MIDlet. The access mode is specified by the boolean parameter writable. To allow other MIDlets (outside of the MIDlet suite) to write on this record store, this parameter must be set to true. Using the first form of the openRecordStore() method will cause the Record store to be accessible only by MIDlets in the same suite (authmode set to AUTHMODE_PRIVATE). To open an existing Record Store from a different MIDlet suite, the third form of the openRecordStore method should be used. The vendorName and the MIDlet suiteName must be specified. If the Record Store is already open, these methods will return the reference to this Record Store. The system keeps track of how many times Record Stores have been opened. Each Record Store must be closed the same number of times it has been opened.

Mobile Application Development

J.E.D.I.

Adding a Record int addRecord(byte[] data, int offset, int numBytes) The method addRecord will create a new Record on the Record Store and return the Record ID. Retrieving Records byte[] getRecord(int recordId) int getRecord(int recordId, byte[] buffer, int offset) int getRecordSize(int recordId) The first form of the getRecord method returns a copy of the data stored on the record given the Record ID. The second form will copy the data on the supplied byte array parameter. When using the second form, the byte array must have been previously allocated. If the size of the record is larger than the size of the parameter, an ArrayIndexOutOfBoundsException will be thrown. You can use the method getRecordSize in order to know beforehand the size of the record you are going to read.

Mobile Application Development

J.E.D.I.

Updating a Record We cannot modify only part of the record data. If we want to modify a record, we have to: 1. read the record using getRecord 2. update the record in memory 3. call setRecord to write the updated record data. void setRecord(int recordId, int numBytes) Deleting a Record void deleteRecord(int recordId) When a record is deleted, the record Id will not be reused in the next calls to addRecord. This means that there could be a gap in the Record Ids. So it is not advisable to use an incrementing counter to list all the records in a record store. The Record Enumerator is what we should use to go through the list of records in a store. Closing a Record Store void closeRecordStore() Calling closeRecordStore() will not close the Record Store until it has been called the same number of times openRecordStore() has been called on a particular store. The system counts how many times a record store is opened. Calling closeRecordStore() more times than it has been opened will cause a RecordStoreNotOpen Exception. byte[] newData, int offset,

Mobile Application Development

J.E.D.I.

This snippet from the example RmsExample1 is a simple MIDlet that demontrates how to create a Record Store, add records and retrieve all the records from the Record Store: // Open or Create Record Store with name "RmsExample1" recStore = RecordStore.openRecordStore("RmsExample1", true); // Load contents of Record Store for (int recId=1; recId<=recStore.getNumRecords(); recId++){ // getRecord returns the length of the record recLength = recStore.getRecord(recId, data, 0); // Convert the byte array into String String item = new String(data, 0, recLength); ...

... // This is the String we will put into the Record String newItem = "Record #" + recStore.getNextRecordID(); // Convert the String into a byte array byte[] bytes = newItem.getBytes(); // Write the Record into the Store recStore.addRecord(bytes, 0, bytes.length); Programming Tips: 1. The starting Record ID is 1, and not 0. When using loops, remember to use 1 as the starting index and not 0. 2. It is better to use a Record Enumerator than using an incrementing index (as in this example). Deleted records will still be read by this example, which will cause an InvalidRecordIDException Exception.

Mobile Application Development

J.E.D.I.

Obtaining the List of Record Stores in a MIDlet Suite static String[] listRecordStores() This method returns an array of the names of record stores owned by the MIDlet suite. If the MIDlet suite does not have a Record Store, this method will return null. String[] storeNames = RecordStore.listRecordStores(); System.out.println("Record Stores for this MIDlet suite:"); for (int i=0; storeNames != null && i<storeNames.length; i++){ System.out.println(storeNames[i]); } Example: RmsListStores Record Stores for this MIDlet suite: Prefs RmsExample1 RmsExample2 Sample Output of RmsListStores The order of the names returned is not defined and is implementation dependent. So if we want to display the names ordered alphabetically, we have to sort the array first.

Mobile Application Development

J.E.D.I.

Storing Java Primitives So far, the data that we have been writing and reading from the record store are Strings. CLDC includes the standard classes for primitive manipulation. These classes are from the standard library of Java 2 Platform, Standard Edition (J2SE). We can write Java primitives by combining the ByteArrayOutputStream and the DataOutputStream classes. Reading primitives (int, long, short, string, boolean, etc) is also done using the ByteArrayInputStream and DataInputStream. ByteArrayOutputStream out = new ByteArrayOutputStream(); DataOutputStream dOut = new DataOutputStream(out); // Store an integer (square of the record id dOut.writeInt(recStore.getNextRecordID() * recStore.getNextRecordID()); // Store a string dOut.writeUTF("Record #" + recStore.getNextRecordID()); byte[] bytes = out.toByteArray(); // Write the Record into the Store recStore.addRecord(bytes, 0, bytes.length); ... // Get the next record byte[] recBytes = enumerator.nextRecord(); ByteArrayInputStream in = new ByteArrayInputStream(recBytes); DataInputStream dIn = new DataInputStream(in); int count = dIn.readInt(); String item = dIn.readUTF();

Mobile Application Development

J.E.D.I.

Other methods for Record Stores long getLastModified() int getVersion() The system remembers when a record store was last modified. The method getLastModified returns the last time a record store was last modified. It returns a long value, in the format used by System.currentTimeMillis(). All record stores maintain a version information. Every time a record is modified, their version number is updated. Using the methods addRecord, setRecord and deleteRecord will cause the version number of record store to be incremented.

static void deleteRecordStore(String recordStoreName) Deletes the named record store. String getName() Returns the name of this RecordStore. int getNextRecordID() Returns the recordId of the next record to be added to the record store. int getNumRecords() Returns the number of records currently in the record store. int getSize() Returns the amount of space, in bytes, that the record store occupies. int getSizeAvailable() Returns the amount of additional available for this record store to grow. room (in bytes)

void setMode(int authmode, boolean writable) Changes the access mode for this RecordStore.

Mobile Application Development

J.E.D.I.

3 Record Enumeration
Traversing a Record Store using an incrementing index is inefficient. Deleted record stores will have to be skipped over since Record Ids of deleted records are not reused. Using a record enumerator solves the problem of having to look out for deleted records. We can also sort the order of the enumeration by passing a comparator method. By passing a filter method, we can also skip records of no interest to our output.

RecordEnumeration enumerateRecords(RecordFilter filter, RecordComparator comparator, boolean keepUpdated) The enumerateRecords method of a record store will return an enumeration for traversing all the records in a store. This is the recommended way to traverse all the records in a record store. We will be discussing the filter and comparator in the next lessons. The simplest way to use this method is to use null for both filter and comparator. This will return an enumeration off all the records in a store in an undefined order. // Open or Create Record Store with name "RmsExample2" recStore = RecordStore.openRecordStore("RmsExample2", true); // Load contents of Record Store RecordEnumeration enumerator = recStore.enumerateRecords(null, null, false); while (enumerator.hasNextElement()){ // Get the next record and convert the byte array into String String item = new String(enumerator.nextRecord()); // Do something with the record ...

Mobile Application Development

10

J.E.D.I.

4 Record Comparator
The ordering of an enumeration can be defined using a Record Comparator. A Record Comparator is passed on the method enumerateRecords. If we want to sort the output of an enumeration, we have to define a comparator and pass that method as a second parameter to enumerateRecords.

int compare(byte[] rec1, byte[] rec2) In order to create a Record Comparator, we must implement the RecordComparator interface. This interface defines a single method, compare(byte[] rec1, byte[] rec2). This method must return, RecordComparator.FOLLOWS or RecordComparator.PRECEDES if rec1 follows or precedes rec2 in the sort order, respectively. RecordComparator.EQUIVALENT should be returned if rec1 is equivalent to rec2 in the sort order. // Create an enumeration, sorted alphabetically RecordEnumeration enumerator = recStore.enumerateRecords(null, new AlphaOrder(), false); ... // Sort alphabetically class AlphaOrder implements RecordComparator { public int compare(byte[] rec1, byte[] rec2){ String record1 = new String(rec1).toUpperCase(); String record2 = new String(rec2).toUpperCase(); if (record1.compareTo(record2) < 0){ return(PRECEDES); } else { if (record1.compareTo(record2) > 0){ return(FOLLOWS); } else { return(EQUIVALENT); } } } }

Mobile Application Development

11

J.E.D.I.

5 Record Filter
The examples we have seen so far reads all the records from the store. We can use a filter to get only the records that we want. Our program must implement the match() method to select our target record(s). It should return true if the record matches our criteria. It should return false otherwise.

boolean matches(byte[] candidate) public boolean matches(byte[] candidate){ boolean isaMatch = false; try { ByteArrayInputStream bin = new ByteArrayInputStream(candidate); DataInputStream dIn = new DataInputStream(bin); int count = dIn.readInt(); String item = dIn.readUTF(); // return only records with contents ending in 0 if (item.endsWith("0")){ isaMatch = true; } else { isaMatch = false; } } catch (Exception e){items.append(e.toString(), null); } return(isaMatch); }

Mobile Application Development

12

J.E.D.I.

6 Exercises
6.1 Preferences Store
Create a class that can persist program preferences. The class will store the preferences into a Record Store. Each record will have a variable name and value. Each variable/value pair is stored in a single record. The name and value are stored in the database as Strings. The class should implement these methods: public String readVar(RecordStore recStore, String name, String defaultValue){ public void writeString(RecordStore recStore, String name, String value);

Mobile Application Development

13