14. Record Stores

MSA devices provide three ways to work with persistent storage.

  • Record stores are tiny databases that contain records. The official name for this API is the Record Management System (RMS).

  • The FileConnection API provides access to the device’s file system.

  • The Personal Information Management (PIM) API allows your application to manipulate the device’s contact list, calendar, and to-do list.

RMS is part of MIDP. In the old days before MSA, RMS was the only persistent storage available to MIDlets. JSR 75 defines both the FileConnection API and the PIM API. Both MSA and MSA subset devices support the FileConnection and PIM APIs.

In the MSA world, RMS and FileConnection both provide persistent storage for MIDlets. FileConnection has some good things going for it:

  • FileConnection is cleaner and simpler than RMS. It’s easier to use from the application side, and its behavior is likely to be more consistent across a range of devices.

  • FileConnection can provide access to images, sounds, and other files on the device that could be useful for your application.

On the other hand, RMS offers three advantages over FileConnection, and the first one is a biggie.

  • Reading and writing files requires appropriate permissions, which means you have to cryptographically sign your application to make sure your users aren’t plagued by security prompts. As you read in Chapter 5, signing is expensive in terms of time and money. By contrast, your application can use RMS for free.

  • If you want your application to run on plain old MIDP devices rather than on MSA devices, the JSR 75 FileConnection API might not be available. Your only option is RMS.

  • If the data you wish to store naturally fits the record model, RMS might be more appropriate than FileConnection for persistent storage.

This chapter is about the RMS API. The next two chapters describe the FileConnection and PIM APIs.

14.1. Itsy Bitsy Teenie Weenie Databases

The RMS API is contained in javax.microedition.rms and centers on the RecordStore class. A record store is something like a database table and consists of records, which are simply byte arrays.

Record stores belong to MIDlet suites. A MIDlet suite’s record stores are not visible to any other MIDlet suites unless they are explicitly shared. In theory, a MIDlet suite’s private record stores should not be visible to any other applications on the device, including native applications. If you store particularly sensitive data in a record store, consider obfuscating or encrypting the data before writing it to a record store.

The RecordStore class contains a group of static methods that are useful for managing record stores. In addition, RecordStore also contains methods for manipulating records inside a record store.

14.2. Working with Record Stores

To open or create a record store, just supply its name to the static openRecordStore() method. A boolean argument determines whether the record store will be created if it does not exist. This example opens or creates a record store named cache:

Image

If something goes wrong, RecordStoreException is thrown. Most methods in RecordStore can throw this exception.

You can get a list of all the record stores belonging to the current MIDlet suite by calling the static listRecordStores(), which returns the names as a string array. This method will not tell you about shared record stores created by other MIDlet suites.

Normally, record stores will be visible only in the MIDlet suite which created them. If you want to create a record store that will be visible to other MIDlet suites, you need to use a fancier version of openRecordStore():

Image

The last two arguments are only used if the record store does not already exist. Use AUTHMODE_ANY to create a record store that will be visible to other MIDlet suites. The other alternative, AUTHMODE_PRIVATE, creates a record store that is visible only in the current MIDlet suite. The fourth argument determines whether other MIDlet suites will be able to make changes in the record store. If it is false, other MIDlet suites will be able to read information from the record store but cannot make changes. When it is true, anyone can make changes.

To open a record store created by another MIDlet suite, you need to know the name of the record store as well as the creating MIDlet suite’s vendor and name. The next example opens a record store named shared-cache, created by a MIDlet suite kickbutt-ch14-other from Funky Chicken:

Image

You can change the sharing and writable aspects of an existing record store with setMode(). It accepts a mode (AUTHMODE_ANY or AUTHMODE_PRIVATE) and a boolean that indicates whether other MIDlet suites can make changes to the record store.

To close an open RecordStore, call closeRecordStore().

To completely remove a record store, pass its name to the static deleteRecordStore() method. If the record store is open when you are trying to remove it, a RecordStoreException is thrown.

Exactly how much space is available for record stores depends a lot on the device. One of the MIDlet suite descriptor values, MIDlet-Data-Size, provides a way for your application to request a certain amount of space for record stores. At runtime, you can call getSizeAvailable() (not static, oddly enough) to find out how much space remains for the current MIDlet suite’s record stores.

If you are using record stores from multiple threads, you might want to find out when a record store is modified. You can do this by supplying an implementation of RecordStoreListener to addRecordStoreListener(). Consult the API documentation for more information.

14.3. Manipulating Records

A record is an array of bytes. Each record has an identification number (ID), which is unique in the record store.

To add a record, pass a byte array to addRecord(). The ID of the new record is returned. To use a portion of the byte array for the record, specify a starting index and a length. The example here uses the entire recordData array.

Image

You can modify an existing record if you know its ID using setRecord(). Likewise, you can pass an ID to deleteRecord() to remove the record from the record store.

To retrieve a record, call getRecord(). A freshly created byte array that contains the record data will be returned:

Image

Alternatively, you can retrieve a record into your own byte array. Find out the size of a record by calling getRecordSize(). The last argument to getRecord() is the starting index in the array where the record data will be placed.

Image

14.4. Making Queries

Most record manipulation is done using record IDs, but when your MIDlet first starts, you have no idea what record IDs are contained in a record store.

You can retrieve a list of records in a record store by calling enumerateRecords(). To retrieve every record in the record store, do this:

Image

The first argument to enumerateRecords() is a RecordFilter implementation. You can use RecordFilter to examine records and return only some subset of them in the RecordEnumeration. The second argument is a RecordComparator. You can provide an implementation if you want the records in the RecordEnumeration to be sorted. Finally, the third argument to enumerateRecords() indicates whether the RecordEnumeration should be kept current with ongoing changes in the underlying record store. Use true for this argument if you expect to be modifying the record store as you’re working your way through the RecordEnumeration.

Bear in mind that if you expect a record store to be accessed from multiple threads, you should implement your own synchronization to make sure everyone’s view of the data remains consistent.

Consult the API documentation for more information about RecordFilter and RecordComparator.

14.5. Iterating through Records

Once you’ve got a RecordEnumeration, you can use it to retrieve the records themselves as well as their IDs.

Use the hasNextElement() and hasPreviousElement() methods to see if you’re at the beginning or end of the RecordEnumeration. When a RecordEnumeration is first returned, it is positioned just before the first record.

Call nextRecord(), previousRecord(), nextRecordId(), and previousRecordId() to navigate through the records.

Interestingly, you cannot retrieve both a record and its ID at the same time. Because you can move forward and backward, however, it is possible to retrieve a record and its ID through some trickery.

14.6. A Place to Keep Your Stuff

The following class encapsulates RMS access, providing a clean interface to store key and value string pairs. It’s useful for storing application preferences.

Image

Image

Image

This class provides a simpleminded implementation that is easy to use from your own application. Each of the public operations (set(), get(), and remove()) is designed to be self-contained, so the record store is never left open. Every set() writes the entire record store on the basis of the contents of an internal Hashtable.

Here is a very simple MIDlet that uses Cache to store a name and a zip code. You can see how easy it is to load the values in startApp():

Image

And it’s just as easy to save the values in destroyApp(). Here is the complete MIDlet (see Figure 14.1 on page 204):

Image

Image

Figure 14.1. The field values are kept in persistent storage.

Image

Each time you run CacheMIDlet, it loads the last name and zip code values from the record store.

14.7. Summary

Record stores are MIDP’s solution for persistent storage. Record stores belong to MIDlet suites, although they can be explicitly shared with other MIDlet suites. Inside a record store, each record is a byte array. Records can be filtered and sorted when they are retrieved from a record store. The Cache example is a simple persistent map of String key and value pairs.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.142.85