⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mar01_ericg.txt

📁 TechTips j2me的常用技巧. 网络功能
💻 TXT
📖 第 1 页 / 共 2 页
字号:

 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 + -