Sei sulla pagina 1di 6

Using RMS on

Motorola J2ME
handsets

July 21, 2004

WHITE PAPER
Using RMS on Motorola handsets
By
Motocoder

E ven though the RMS API’s are fairly simple to use and haven't changed much since MIDP 1.0, a
developer may see some unexpected results when porting their application across different
platforms. This guide outlines some tips to ensure portability and general usability for the end user, and
gives guidance on how to cope with common problems such as data corruption or data loss.

RMS introduction
“The Mobile Information Device Profile provides a mechanism for MIDlets to persistently store data
and later retrieve it. This persistent storage mechanism is modeled after a simple record oriented
database and is called the Record Management System.” [1]
Using the RMS to store persistent data in your application may seem simple at a first glance. Applying
some of the guidelines below may increase the user experience of your application and help you to
prevent common mistakes when building an application for Motorola phones.

Performance
There are numerous devices on the market supporting MIDP and they have different hardware and
software affecting how the RMS acts in different situations. Performance is one of the areas where
devices vary a lot. When developing applications using RMS to store persistent data, developers should
always keep in mind that the target device might not be as fast as the PC emulator or even another
phone. Just storing a single byte to persistent memory may seem to be a simple task, but there are a lot
of hardware and software factors that affects the performance of this operation. In the article “J2ME
devices: Real-world performance” at www.javaworld.com [2] shows that the performance of RMS
operations can vary from microseconds to several seconds depending on the target device.

Performance measurement depends not only on hardware and Virtual Machine implementation,
but also on the application doing the benchmarking. Some operations may be performed in
different ways to suit the underlying implementation better or worse and may have significant
effect on performance results. The above reference is provided only to emphasize the difference
in performance of various device platforms.

Due to the varying performance of RMS operations across platforms, developers are strongly
encouraged to display a dialog of some sort informing the end user that the application is busy while
managing the persistent storage.
Data Loss
Some user actions such as removing the battery while an application is running may result in data loss of
an application’s RecordStores. A well written application can recover from such a situation and
continue functioning. For Motorola devices specifically, developers should be aware that keeping a
RecordStore object in the open state throughout the application lifetime greatly increases the chance
of encountering this issue. To resolve this, developers are encouraged to close the RecordStore object
as soon as the block of read and/or write operations are done.

Threading and Thread safety


Many applications use threads in order to have concurrent execution of tasks and to improve the user
experience of the application. However, this may also increase the complexity involved in managing an
application’s RMS data. The paragraph quoted below is from the MIDP specification, highlighting the
need for synchronizing RMS access within an application’s threads.
“No locking operations are provided in this API. Record store implementations ensure that all
individual record store operations are atomic, synchronous, and serialized, so no corruption will occur
with multiple accesses. However, if a MIDlet uses multiple threads to access a record store, it is the
MIDlet's responsibility to coordinate this access or unintended consequences may result. Similarly, if a
platform performs transparent synchronization of a record store, it is the platform's responsibility to
enforce exclusive access to the record store between the MIDlet and synchronization engine.” [1]
Another section from the MIDP specification often overlooked is one about implementing a
CommandListener and its commandAction() method.
“The specification does not require the platform to create several threads for the event delivery. Thus, if
a CommandListener method does not return or the return is not delayed, the system may be blocked. So,
there is the following note to application developers: the CommandListener method should return
immediately.” [3]
This in conjunction with performance differences on platforms, an unfortunate mix of a
CommandListener and RMS operations could block the system and make the application very user un-
friendly.
Below is a code snippet showing how you can trigger an RMS operation from a CommandListener.

public class RMSTest extends MIDlet implements CommandListener {


static final int RMS_ACTION_ADD = 1;
static final int RMS_ACTION_LIST = 2;

private RMSThread m_rmst;

/* Do main class and form initialization here */

public void commandAction(Command c, Displayable s) {


if (c == exitCommand) {
destroyApp(false);
}
else if( c == addRms ) {
m_rmst = null;
System.gc();
m_frm.deleteAll();
m_rmst = new RMSThread( RMSTest.RMS_ACTION_ADD );
m_rmst.start();
}
else if( c == listRms ) {
m_rmst = null;
System.gc();
m_frm.deleteAll();
m_rmst = new RMSThread( RMSTest.RMS_ACTION_LIST );
m_rmst.start();
}
}

public void dbgout( String s ) {


m_frm.append( s+"\n" );
}

class RMSThread extends Thread {


private int m_action;
public RMSThread( int action ) {
m_action = action;
}

private void addRecord() {


RecordStore db = null;
RecordEnumeration enum = null;
String writestr = "RMSTest";
byte record[];

try {
dbgout( "open recordstore and write one record" );
db = RecordStore.openRecordStore( "test", true );
db.addRecord( writestr.getBytes(), 0, writestr.length() );
db.closeRecordStore();
dbgout( "done" );
}
catch( Throwable t ) {
dbgout( t.toString() );
}
}

private void listRecords() {


RecordStore db = null;
RecordEnumeration enum = null;
String writestr = "RMSTest";
byte record[];

try {
dbgout( "List records in RecordStore" );
db = RecordStore.openRecordStore( "test", false );
enum = db.enumerateRecords( null, null, false );
while( enum.hasNextElement() ) {
record = enum.nextRecord();
dbgout( new String( record ) );
}
enum.destroy();
db.closeRecordStore();
dbgout( "done" );
}
catch( Throwable t ) {
dbgout( t.toString() );
}
}
public void run() {
switch( m_action ) {
case RMSTest.RMS_ACTION_ADD:
addRecord();
break;
case RMSTest.RMS_ACTION_LIST:
listRecords();
break;
}
}
}
}

There are a number of ways you can launch a new thread to perform your RMS operation or other type
of task, the above example is just to show you the concept of it. In fact the example above can be
optimized so that it doesn’t create a new thread for each of these tasks, but instead adds the task to a
queue in the existing RMSThread, which is then served when the Thread is ready for it. That would
reduce some overhead in creating, starting and stopping new threads.

Motorola SDK Emulator notes


Under certain circumstances an application run on the Emulator7.2 or Emulator 7.5 environment may
experience strange errors when using RMS functions. This is usually caused by some temporary files in
Emulator7.x\bin\1\RSC not being deleted after a previous run of a different application. Removing the
entire 1\RSC directory structure will resolve this problem.

This should ONLY be done if you are experiencing problems with your application using RMS
operations such as creating a RecordStore even though the application doesn’t have any
previous RecordStore objects created.

This is ONLY applicable to the emulator!

Conclusion
By following the guidelines below, the user experience of an application can be greatly improved
together with the portability of an application across various MIDP platforms.

Make sure the MIDlet displays a “please wait” dialog during RMS operations or other lengthy task.
Make sure the MIDlet can cope with corrupted RMS data (i.e. gracefully recover and reconstruct the
database).
Make sure the MIDlet synchronizes access of RMS operations from multiple threads.
CommandListener implementations should create a separate thread to perform any RMS operations.
Don’t keep RecordStore objects open.
Emulator7.x based emulators may generate false RecordStoreException when creating databases.

References
1. JSR 37, JSR 118, RecordStore class description. http://java.sun.com/products/midp/
2. J2ME devices: Real-world performance. http://www.javaworld.com/javaworld/jw-10-2002/jw-1025-
j2mebenchmark.html
3. JSR 37, JSR 118, CommandListener interface description. http://java.sun.com/products/midp/

Potrebbero piacerti anche