📄 mar01_ericg.txt
字号:
J 2 M E T E C H T I P S
TIPS, TECHNIQUES, AND SAMPLE CODE
WELCOME to the Java Developer Connection(sm) (JDC)
Java(tm) 2 Platform, Micro Edition (J2ME(tm))
Tech Tips, for March 19, 2001. This issue covers:
* Using Record Stores Efficiently
* Using the MIDP Low-Level User Interface API
The J2ME Tech Tips are written by Eric Giguere
(http://www.ericgiguere.com), an engineer at iAnywhere
Solutions, inc, and author of the book "Java 2 Micro
Edition: Professional Developer's Guide."
You can view this issue of the J2ME Tech Tips on the Web at
http://java.sun.com/jdc/J2METechTips/2001/tt0319.html
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
USING RECORD STORES EFFICIENTLY
The February 20, 2001 J2ME Tech Tip "Record Management System
Basics" introduced the Record Management System (RMS), a set of
classes for persistently storing and retrieving data. RMS is
defined in the Mobile Information Device Profile (MIDP). If you
read the tip, you discovered how simple it is to create and use
record stores. This tip focuses on getting data into and out of
a record store as quickly and efficiently as possible.
The data in a record store is stored in chunks called records.
A record is a set of arbitrary binary data, basically an array
of bytes. You always read or write an entire record at once,
using the getRecord, addRecord and setRecord methods of the
RecordStore class. Here's the general pattern:
RecordStore rs = null;
byte[] data;
int id = ....; // assign a record ID
try {
rs = RecordStore.openRecordStore( "mydata", false );
data = rs.getRecord( id );
..... // modify the data
rs.setRecord( id, data, 0, data.length );
rs.closeRecordStore();
}
catch( Exception e ){
// do something here
}
There's no way to change just part of a record: you must read in
the entire record, make the change to the data in memory, and
then write the entire record back to the record store.
This brings up a good rule regarding using data stores
efficiently: minimize the number of times you read or write data
to a record store. At the very least, minimize the number of
writes; on some devices a write operation is much more expensive
than a read operation. Palm devices, for example, write-protect
most of their memory to prevent applications from corrupting each
other's data. In such an environment, the fewer writes you do,
the faster the application runs.
Even if you limit the number of times you write data back to a
record store, you might find that reading the data becomes a
bottleneck in your application. This is especially true when
enumerating the records. Part of the problem often lies in the
excessive object "churning" that occurs. Churning refers to the
act of continually creating and discarding objects (including
arrays) from the memory heap. Not only is object creation
expensive as far as time, but churning can also overwhelm the
garbage collector.
Here, for example, is one way of cycling through all the records
in a record store:
RecordStore rs = ....; // an open record store
try {
int lastID = rs.getNextRecordID();
byte[] data;
for( int i = 0; i < lastID; ++i ){
try {
data = rs.getRecord( i );
.... // do something with the data
}
catch( InvalidRecordIDException e ){
continue;
}
}
}
catch( Exception e ){
// error
}
Another way is to use an enumeration:
RecordStore rs = ....; // an open record store
try {
RecordEnumeration enum = rs.enumerateRecords(
null, null, false );
while( enum.hasNextElement() ){
byte[] data = enum.nextRecord();
.... // do something with the data
}
}
catch( Exception e ){
// error
}
The problem with both approaches is that they allocate new array
objects for each record, a classic case of object churning. A
better approach is to reuse the same array, discarding it only
when it's too small. For example:
RecordStore rs = ....; // an open record store
try {
RecordEnumeration enum = rs.enumerateRecords(
null, null, false );
byte[] data = new byte[100];
int len = 0;
while( enum.hasNextElement() ){
int id = enum.nextRecordId();
len = rs.getRecordSize( id );
if( len > data.length ){
// add a growth factor
data = new byte[ len + 40 ];
}
rs.getRecord( id, data, 0 );
// do something with the data
}
}
catch( Exception e ){
// error
}
Notice how the array automatically grows to accommodate the size
of a record. Of course, if each record is the same (fixed) size,
then you don't even have to worry about growing the array.
The second rule is when reading records, reuse byte arrays
if possible. The same rule about reusing byte arrays also holds
for writing records. However, object churning isn't always a
problem when writing data because records tend to be read more
than they're written.
Reusing the same array isn't usually enough to avoid object
churning. This is because the recommended way to read the data
is to use a DataInputStream object, as in the following example:
byte[] data = .....; // data from a record store
ByteArrayInputStream bin = new ByteArrayInputStream( data );
DataInputStream din = new DataInputStream( bin );
String name = din.readUTF();
boolean isFemale = din.readBoolean();
// etc. etc.
If you create a new set of input streams for each record you
read, then you're still creating and discarding too many objects.
However, if you reuse the raw byte array, you can also reuse the
input streams. You can do this by using the reset method to make
the streams read from the beginning of the array. For example,
consider reading a set of records, where each record consists of
a boolean value and two integer values:
RecordStoreEnumeration enum = ...; // get a record enumeration
byte[] data = new byte[9]; // record size
ByteArrayInputStream bin = new ByteArrayInputStream( data );
DataInputStream din = new DataInputStream( bin );
while( enum.hasNextElement() ){
int id = enum.nextRecordId();
getRecord( id, data, 0 );
din.reset(); // move stream back to start
boolean first = din.readBoolean();
int second = din.readInt();
int third = din.readInt();
// do something here
}
This is a more efficient way to read records. So the third rule
of efficient record usage is: reuse the input streams whenever
you reuse the raw byte array.
Let's wrap up the discussion of efficient record usage by writing
a simple Record class that you can use to read records:
import java.io.*;
import javax.microedition.rms.*;
public class Record implements DataInput {
private RecordStore _rs;
private byte[] _data;
private int _length;
private int _id;
private DataInputStream _din;
public Record( RecordStore rs ){
this( rs, 100 );
}
public Record(
RecordStore rs, int initialRecordSize ){
_rs = rs;
_data = new byte[ initialRecordSize ];
_din = new DataInputStream(
new ByteArrayInputStream( _data ) );
_length = -1;
}
public byte[] getByteArray() { return _data; }
public int getLength() { return _length; }
public byte[] moveTo( int id )
throws RecordStoreNotOpenException,
InvalidRecordIDException,
RecordStoreException,
IOException
{
_length = _rs.getRecordSize( id );
if( _length > _data.length ){
_data = new byte[ _length + 40 ];
_din = new DataInputStream(
new ByteArrayInputStream( _data ) );
}
_rs.getRecord( id, _data, 0 );
_id = id;
_din.reset();
return _data;
}
public void readFully(byte b[])
throws IOException {
_din.readFully( b );
}
public void readFully(byte b[], int off, int len)
throws IOException {
_din.readFully( b, off, len );
}
public int skipBytes(int n) throws IOException {
return _din.skipBytes( n );
}
public boolean readBoolean() throws IOException {
return _din.readBoolean();
}
public byte readByte() throws IOException {
return _din.readByte();
}
public int readUnsignedByte()
throws IOException {
return _din.readUnsignedByte();
}
public short readShort() throws IOException {
return _din.readShort();
}
public int readUnsignedShort()
throws IOException {
return _din.readUnsignedShort();
}
public char readChar() throws IOException {
return _din.readChar();
}
public int readInt() throws IOException {
return _din.readInt();
}
public long readLong() throws IOException {
return _din.readLong();
}
public String readUTF() throws IOException {
return _din.readUTF();
}
}
You then use the class like this:
try {
rs = RecordStore.openRecordStore( "mydata", true );
// Write two records to the record store
ByteArrayOutputStream bout =
new ByteArrayOutputStream();
DataOutputStream dout =
new DataOutputStream( bout );
byte[] data;
dout.writeUTF( "this is a test" );
dout.writeInt( 1 );
dout.flush();
data = bout.toByteArray();
rs.addRecord( data, 0, data.length );
bout.reset();
dout.writeUTF( "this is another test" );
dout.writeInt( 99 );
dout.flush();
data = bout.toByteArray();
rs.addRecord( data, 0, data.length );
// Now read through the record store
Record record = new Record( rs );
int lastID = rs.getNextRecordID();
RecordEnumeration enum = rs.enumerateRecords(
null, null, false );
while( enum.hasNextElement() ){
int id = enum.nextRecordId();
record.moveTo( id );
System.out.println( record.readUTF() + " " +
record.readInt() );
}
rs.closeRecordStore();
}
catch( Exception e ){
// handle error
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -