数据库和 MIDP
本系列中的 第二部分介绍了数据映射的基本知识。您学会了如何在字节数组(byte array)中存储原始数据类型的数值,如何使用流向记录库(recordstore)中存储对象和从记录库中检索对象以及如何从字节数组中抽取已存储的数值。在本文中,您将学会如何使您的应用程序远离这些低级操作,方法是对核心类进行扩展使其包含读写操作,创建并使用字段列表来存储和检索对象。
扩展核心类
J2ME 开发人员的目标之一是最小化应用程序对内存的使用。理想情况下,在任何给定的时间内,内存中应该只含有某个数据块的一个副本。然而,当您向 ByteArrayOutputStream
写数据时,您从来无法访问其基本的字节数组--调用toByteArray()
返回该数组的一个拷贝。如果您紧接着就将该字节数组保存到记录库中,则会造成没必要的内存浪费。要直接访问该数组,只需要对类ByteArrayOutputStream
进行简单的扩展即可,扩展后的类称为DirectByteArrayOutputStream
:
package j2me.io; import java.io.*; // A version of ByteArrayOutputStream that gives you // direct access to the underlying byte array if // you need it. public class DirectByteArrayOutputStream extends ByteArrayOutputStream { // Constructs a byte array output stream of default size public DirectByteArrayOutputStream(){ super(); } // Constructs a byte array output stream of given size public DirectByteArrayOutputStream( int size ){ super( size ); } // Returns a reference to the underlying byte array. // The actual amount of data in the byte array is // obtained via the size method. public synchronized byte[] getByteArray(){ return buf; } // Swaps in a new byte array for the old one, resetting // the count as well. public synchronized byte[] swap( byte[] newBuf ){ byte[] oldBuf = buf; buf = newBuf; reset(); return oldBuf; } }
记住调用 size()
方法查看存储在字节数组中的实际数据的多少:
... DirectByteArrayOutputStream bout = ... RecordStore rs = ... int numBytes = bout.size(); byte[] data = bout.getByteArray(); rs.addRecord( data, 0, numBytes ); ...
未来保持一致性,您可以用类似的方法对 ByteArrayInputStream
类进行扩展:
package j2me.io; import java.io.*; // A version of ByteArrayInputStream that lets you // replace the underlying byte array. public class DirectByteArrayInputStream extends ByteArrayInputStream { // Constructs an output stream from the given array public DirectByteArrayInputStream( byte buf[] ){ super( buf ); } // Constructs an output stream from the given subarray public DirectByteArrayInputStream( byte buf[], int offset, int length ){ super( buf, offset, length ); } // Resets the array the stream reads from public synchronized void setByteArray( byte[] buf ){ this.buf = buf; this.pos = 0; this.count = buf.length; this.mark = 0; } // Resets the array the stream reads from public synchronized void setByteArray( byte[] buf, int offset, int length ){ this.buf = buf; this.pos = offset; this.count = Math.min( offset + length, buf.length ); this.mark = offset; } }
注意类ByteArrayInputStream
和类ByteArrayOutputStream
以及以上所写的两个扩展类都使用了用于线程安全的同步方法。尽管在大多数情况下只有单一线程使用这些流,同步也就没有必要了。如果您的应用程序需要读写大量的数据,可考虑创建基于这些类的非同步的版本以获得较快的速度。
您也可以很容易的对 DataInputStream
和DataOutputStream
进行扩展。例如,如果您需要在多个地方写整型数组,请使用DataOutputStream
的以下扩展:
package j2me.io; import java.io.*; public class ExtendedDataOutputStream extends DataOutputStream { public ExtendedDataOutputStream( OutputStream out ){ super( out ); } public final void writeIntArray( int[] arr ) throws IOException { int size = arr.length; writeInt( size ); for( int i = 0; i < size; ++i ){ writeInt( arr[i] ); } } }
相反,您可以将这种代码放入一个帮助器类,因为您不需要访问任何的 protected members。
创建记录字段
现在您已经拥有了创建基于字段的记录库的所有工具,记录库中的每条记录都是一组指定类型的命名字段。您利用两个类来管理该记录库。其中的第一个类 FieldList
管理字段自身的信息--元数据:
package j2me.rms; import java.io.*; import javax.microedition.rms.*; // Maintains information about the fields in a // field-based record store. Currently just a list of // field types and (optional) field names, but could // easily be expanded to store other information. public class FieldList { private static final int VERSION = 1; // The basic field types. public static final byte TYPE_BOOLEAN = 1; public static final byte TYPE_BYTE = 2; public static final byte TYPE_CHAR = 3; public static final byte TYPE_SHORT = 4; public static final byte TYPE_INT = 5; public static final byte TYPE_LONG = 6; public static final byte TYPE_STRING = 7; // Constructs an empty list. public FieldList(){ } // Constructs a list of the given size. public FieldList( int numFields ){ if( numFields < 0 || numFields > 255 ){ throw new IllegalArgumentException( "Bad number of fields" ); } _types = new byte[ numFields ]; _names = new String[ numFields ]; } // Returns the number of fields. public int getFieldCount(){ return _types != null ? _types.length : 0; } // Returns the name of a field. public String getFieldName( int index ){ String name = _names[ index ]; return name != null ? name : ""; } // Returns the type of a field. public byte getFieldType( int index ){ return _types[ index ]; } // Reads the field list from a byte array. public void fromByteArray( byte[] data ) throws IOException { ByteArrayInputStream bin = new ByteArrayInputStream( data ); fromDataStream( new DataInputStream( bin ) ); bin.close(); } // Reads the fields list from a data stream. public void fromDataStream( DataInputStream din ) throws IOException { int version = din.readUnsignedByte(); if( version != VERSION ){ throw new IOException( "Incorrect version " + version + " for FieldList, expected " + VERSION ); } int numFields = din.readUnsignedByte(); _types = new byte[ numFields ]; _names = new String[ numFields ]; if( numFields > 0 ){ din.readFully( _types ); for( int i = 0; i < numFields; ++i ){ _names[i] = din.readUTF(); } } } // Reads a field list from a record store. public void fromRecordStore( RecordStore rs, int index ) throws IOException, RecordStoreException { fromByteArray( rs.getRecord( index ) ); } // Sets the name of a field. public void setFieldName( int index, String name ){ _names[ index ] = name; } // Sets the type of a field. public void setFieldType( int index, byte type ){ _types[ index ] = type; } // Stores the fields list to a byte array public byte[] toByteArray() throws IOException { ByteArrayOutputStream bout = new ByteArrayOutputStream(); toDataStream( new DataOutputStream( bout ) ); byte[] data = bout.toByteArray(); bout.close(); return data; } // Stores the fields list to a data stream public void toDataStream( DataOutputStream out ) throws IOException { out.writeByte( VERSION ); int count = getFieldCount(); out.writeByte( count ); if( count > 0 ){ out.write( _types, 0, count ); for( int i = 0; i < count; ++i ){ out.writeUTF( getFieldName( i ) ); } } } // Writes a field list to a record store. public int toRecordStore( RecordStore rs, int index ) throws IOException, RecordStoreException { byte[] data = toByteArray(); boolean add = true; if( index > 0 ){ try { rs.setRecord( index, data, 0, data.length ); add = false; } catch( InvalidRecordIDException e ){ } } // If the record doesn't actually exist yet, // go ahead and create it by inserting dummy // records ahead of it if( add ){ synchronized( rs ){ int nextID = rs.getNextRecordID(); if( index <= 0 ) index = nextID; while( nextID < index ){ rs.addRecord( null, 0, 0 ); } if( nextID == index ){ rs.addRecord( data, 0, data.length ); } } } return index; } private String[] _names; private byte[] _types; }
实际上,一个 FieldList
实例只是两个数组的一个包装器。
在其核心,一个 FieldList
实例只是两个数组的一个包装器。第一个数组存储每个字段的类型,第二个数组存储每个字段的名称。名称是可选的,重要的是类型,因为它决定如何将数据写入记录和从记录中读出。所有标准的 Java 原始数据类型都支持,还有String
类型。下面是对存储某个组织的部门列表的字段的定义:
... FieldList depts = new FieldList( 3 ); depts.setFieldType( 0, FieldList.TYPE_SHORT ); depts.setFieldName( 0, "ID" ); depts.setFieldType( 1, FieldList.TYPE_STRING ); depts.setFieldName( 1, "Name" ); depts.setFieldType( 2, FieldList.TYPE_INT ); depts.setFieldName( 2, "ManagerID" ); ...
一个 FieldList
实例可存储在一个数据流、一个字节数组或者一个记录库中。如果您将字段列表存储为记录库中的第一条记录,则任何可打开该记录库的代码就可以读取该记录以决定其余字段的字段布局。要使这个策略起到作用,您必须在创建记录库后立即存储记录列表:
... FieldList list = ... // a field list RecordStore rs = RecordStore.openRecordStore( "foo", true ); if( rs.getNumRecords() == 0 ){ // empty, store it list.toRecordStore( rs, -1 ); } ...
toRecordStore()
方法的第二个参数确定了保存字段数据所使用的记录。负值表示将要添加一条新的记录。
管理基于字段的记录库所需的第二个类是 FieldBasedStore
,它将管理实际的读写操作:
package j2me.rms; import java.io.*; import javax.microedition.rms.*; import j2me.io.*; // A wrapper class for a record store that allows the // records to be accessed as a set of fields. The field // definitions are maintained separately using a FieldList // object, which can be stored as part of the record store // or separately. public class FieldBasedStore { // Some useful constants public static Boolean TRUE = new Boolean( true ); public static Boolean FALSE = new Boolean( false ); // Markers for the types of string we support private static final byte NULL_STRING_MARKER = 0; private static final byte UTF_STRING_MARKER = 1; // Constructs a field store where the field list is // assumed to be stored in the first record. public FieldBasedStore( RecordStore rs ) throws IOException, RecordStoreException { this( rs, 1 ); } // Constructs a field store where the field list is // stored in the given record. public FieldBasedStore( RecordStore rs, int fieldListID ) throws IOException, RecordStoreException { this( rs, loadFieldList( rs, fieldListID ) ); } // Constructs a field store with the given field list. public FieldBasedStore( RecordStore rs, FieldList list ){ _rs = rs; _fieldList = list; } // Adds a new record to the store. Returns the new // record ID. public synchronized int addRecord( Object[] fields ) throws IOException, RecordStoreException { writeStream( fields ); byte[] data = _bout.getByteArray(); return _rs.addRecord( data, 0, data.length ); } // Returns the current field list. public FieldList getFieldList(){ return _fieldList; } // Returns the record store. public RecordStore getRecordStore(){ return _rs; } // Loads the field list from the record store. private static FieldList loadFieldList( RecordStore rs, int fieldListID ) throws IOException, RecordStoreException { FieldList list = new FieldList(); list.fromRecordStore( rs, fieldListID ); return list; } // Prepares the store for input by making sure that // the data buffer is big enough. The streams are // reused. private void prepareForInput( int size ){ if( _buffer == null || _buffer.length < size ){ _buffer = new byte[ size ]; } if( _bin == null ){ _bin = new DirectByteArrayInputStream( _buffer ); _din = new DataInputStream( _bin ); } else { _bin.setByteArray( _buffer ); } } // Prepares the store for output. The streams are reused. private void prepareForOutput(){ if( _bout == null ){ _bout = new DirectByteArrayOutputStream(); _dout = new DataOutputStream( _bout ); } else { _bout.reset(); } } // Reads a field from the buffer. private Object readField( int type ) throws IOException { switch( type ){ case FieldList.TYPE_BOOLEAN: return _din.readBoolean() ? TRUE : FALSE; case FieldList.TYPE_BYTE: return new Byte( _din.readByte() ); case FieldList.TYPE_CHAR: return new Character( _din.readChar() ); case FieldList.TYPE_SHORT: return new Short( _din.readShort() ); case FieldList.TYPE_INT: return new Integer( _din.readInt() ); case FieldList.TYPE_LONG: return new Long( _din.readLong() ); case FieldList.TYPE_STRING: { byte marker = _din.readByte(); if( marker == UTF_STRING_MARKER ){ return _din.readUTF(); } } } return null; } // Reads the record at the given ID and returns it as // a set of objects that match the types in the // field list. public synchronized Object[] readRecord( int recordID ) throws IOException, RecordStoreException { prepareForInput( _rs.getRecordSize( recordID ) ); _rs.getRecord( recordID, _buffer, 0 ); int count = _fieldList.getFieldCount(); Object[] fields = new Object[ count ]; for( int i = 0; i < count; ++i ){ fields[i] = readField(_fieldList.getFieldType(i)); } return fields; } // Converts an object to a boolean value. public static boolean toBoolean( Object value ){ if( value instanceof Boolean ){ return ((Boolean) value).booleanValue(); } else if( value != null ){ String str = value.toString().trim(); if( str.equals( "true" ) ) return true; if( str.equals( "false" ) ) return false; return( toInt( value ) != 0 ); } return false; } // Converts an object to a char. public static char toChar( Object value ){ if( value instanceof Character ){ return ((Character) value).charValue(); } else if( value != null ){ String s = value.toString(); if( s.length() > 0 ){ return s.charAt( 0 ); } } return 0; } // Converts an object to an int. This code // would be much simpler if the CLDC supported // the java.lang.Number class. public static int toInt( Object value ){ if( value instanceof Integer ){ return ((Integer) value).intValue(); } else if( value instanceof Boolean ){ return ((Boolean) value).booleanValue() ? 1 : 0; } else if( value instanceof Byte ){ return ((Byte) value).byteValue(); } else if( value instanceof Character ){ return ((Character) value).charValue(); } else if( value instanceof Short ){ return ((Short) value).shortValue(); } else if( value instanceof Long ){ return (int) ((Long) value).longValue(); } else if( value != null ){ try { return Integer.parseInt( value.toString() ); } catch( NumberFormatException e ){ } } return 0; } // Converts an object to a long. This code // would be much simpler if the CLDC supported // the java.lang.Number class. public static long toLong( Object value ){ if( value instanceof Integer ){ return ((Integer) value).longValue(); } else if( value instanceof Boolean ){ return ((Boolean) value).booleanValue() ? 1 : 0; } else if( value instanceof Byte ){ return ((Byte) value).byteValue(); } else if( value instanceof Character ){ return ((Character) value).charValue(); } else if( value instanceof Short ){ return ((Short) value).shortValue(); } else if( value instanceof Long ){ return ((Long) value).longValue(); } else if( value != null ){ try { return Long.parseLong( value.toString() ); } catch( NumberFormatException e ){ } } return 0; } // Writes a field to the output buffer. private void writeField( int type, Object value ) throws IOException { switch( type ){ case FieldList.TYPE_BOOLEAN: _dout.writeBoolean( toBoolean( value ) ); break; case FieldList.TYPE_BYTE: _dout.write( (byte) toInt( value ) ); break; case FieldList.TYPE_CHAR: _dout.writeChar( toChar( value ) ); break; case FieldList.TYPE_SHORT: _dout.writeShort( (short) toInt( value ) ); break; case FieldList.TYPE_INT: _dout.writeInt( toInt( value ) ); break; case FieldList.TYPE_LONG: _dout.writeLong( toLong( value ) ); break; case FieldList.TYPE_STRING: if( value != null ){ String str = value.toString(); _dout.writeByte( UTF_STRING_MARKER ); _dout.writeUTF( str ); } else { _dout.writeByte( NULL_STRING_MARKER ); } break; } } // Writes a set of fields to the given record. The // fields must be compatible with the types in // the field list. public synchronized void writeRecord( int recordID, Object[] fields ) throws IOException, RecordStoreException { writeStream( fields ); byte[] data = _bout.getByteArray(); _rs.setRecord( recordID, data, 0, data.length ); } // Writes a set of fields to the output stream. private void writeStream( Object[] fields ) throws IOException { int count = _fieldList.getFieldCount(); int len = ( fields != null ? fields.length : 0 ); prepareForOutput(); for( int i = 0; i < count; ++i ){ writeField( _fieldList.getFieldType( i ), ( i < len ? fields[i] : null ) ); } } private DirectByteArrayInputStream _bin; private DirectByteArrayOutputStream _bout; private byte[] _buffer; private DataInputStream _din; private DataOutputStream _dout; private FieldList _fieldList; private RecordStore _rs; }
要创建一个 FieldBasedStore
,你需要一个RecordStore
实例和一个FieldList
实例。您可以在构造FieldBasedStore
的过程中隐式地从记录库自身中读取后者:
... RecordStore rs = ... // an open record store FieldBasedStore fstore = new FieldBasedStore( rs ); ...
或者您可以对其进行显式指定:
... RecordStore rs = ... // an open record store FieldList list = ... // a field list FieldBasedStore fstore = new FieldBasedStore( rs, list ); ...
FieldBasedStore
将每条记录作为一个对象数组来处理。数组中的数据类型和字段列表中所描述的字段类型相匹配。在上面的部门列表中,每一个都含有一个部门标识符、一个部门名称以及一个经理标识符。您可以用如下方法添加一条记录:
... Object[] fields = new Object[]{ new Short( 1 ), "Accounting", new Integer( 100 ) }; int recordID = fstore.addRecord( fields ); ...
注意 FieldBasedStore
中的写记录代码是具有智能性的,它可以执行“明显的”数据转换,所以您也可以用以下方法进行:
... Object[] fields = new Object[]{ "1", "Accounting", "100" }; int recordID = fstore.addRecord( fields ); ...
读取记录也同样简单:
... Object[] fields = fstore.readRecord( recordID ); for( int i = 0; i < fields.length; ++i ){ System.out.println( "Field: " + fstore.getFieldList().getFieldName( i ) + " Value: " + fields[i] ); } ...
您可以在任何时候重写该数组来修改记录:
... Object[] fields = fstore.readRecord( recordID ); fields[2] = "134"; // change the manager fstore.writeRecord( recordID, fields ); ...
这里有一个 MIDlet 的示例,它使用了一对基于字段的记录库来存储和检索雇员和部门数据。它也可使用
第一部分 所述的 RMSAnalyzer
类将记录库中的内容倒出,只是向您表明记录是如何被存储的。
import java.io.*; import java.util.*; import javax.microedition.lcdui.*; import javax.microedition.midlet.*; import javax.microedition.rms.*; import j2me.rms.*; // A simple MIDlet for testing RMS mappings // done using the FieldBasedStore class. public class RMSMappings extends MIDlet implements CommandListener { private Display display; public static final Command exitCommand = new Command( "Exit", Command.EXIT, 1 ); public static final Command testCommand = new Command( "Test", Command.SCREEN, 1 ); private static Object[][] empList = { new Object[]{ "1", "Mary", "CEO", "100", "F" }, new Object[]{ "2", "John", "CFO", "200", "M" }, new Object[]{ "3", "Pat", "Closem", "300", "F" }, new Object[]{ "4", "PJ", "Admin", "100", "M" }, }; private static Object[][] deptList = { new Object[]{ "100", "Executive", "1" }, new Object[]{ "200", "Operations", "2" }, new Object[]{ "300", "Sales", "1" }, }; public RMSMappings(){ } public void commandAction( Command c, Displayable d ){ if( c == exitCommand ){ exitMIDlet(); } else if( c == testCommand ){ runTest(); } } protected void destroyApp( boolean unconditional ) throws MIDletStateChangeException { exitMIDlet(); } public void exitMIDlet(){ notifyDestroyed(); } public Display getDisplay(){ return display; } protected void initMIDlet(){ display.setCurrent( new MainForm() ); } protected void pauseApp(){ } private void printRecord( FieldBasedStore store, int recordID ){ try { FieldList list = store.getFieldList(); Object[] fields = store.readRecord( recordID ); if( fields.length != list.getFieldCount() ){ System.out.println( "Error: bad count" ); return; } System.out.println( "Record " + recordID + ":" ); for( int i = 0; i < fields.length; ++i ){ System.out.println( " " + list.getFieldName( i ) + ": " + fields[i] ); } } catch( RecordStoreException e ){ } catch( IOException e ){ } } private void runTest(){ // First delete the record stores... System.out.println( "Deleting record stores..." ); String[] names = RecordStore.listRecordStores(); for( int i = 0; i < names.length; ++i ){ try { RecordStore.deleteRecordStore( names[i] ); } catch( RecordStoreException e ){ System.out.println( "Could not delete " + names[i] ); } } // Create two record stores, one with a field list // stored in the first record and the second with // a field list stored separately (in the app) RecordStore empRS = null; RecordStore deptRS = null; FieldList empFields = new FieldList( 5 ); FieldList deptFields = new FieldList( 3 ); FieldBasedStore employees; FieldBasedStore departments; empFields.setFieldType( 0, FieldList.TYPE_INT ); empFields.setFieldName( 0, "ID" ); empFields.setFieldType( 1, FieldList.TYPE_STRING ); empFields.setFieldName( 1, "Given Name" ); empFields.setFieldType( 2, FieldList.TYPE_STRING ); empFields.setFieldName( 2, "Last Name" ); empFields.setFieldType( 3, FieldList.TYPE_BOOLEAN ); empFields.setFieldName( 3, "Active" ); empFields.setFieldType( 4, FieldList.TYPE_CHAR ); empFields.setFieldName( 4, "Sex" ); System.out.println( "Initializing employees" ); try { empRS = RecordStore.openRecordStore( "empRS", true ); // now store the field list in the RS empFields.toRecordStore( empRS, -1 ); employees = new FieldBasedStore( empRS ); } catch( RecordStoreException e ){ System.out.println( "Could not create empRS" ); return; } catch( IOException e ){ System.out.println( "Error storing field list" ); return; } System.out.println( "Initializing departments" ); deptFields.setFieldType( 0, FieldList.TYPE_INT ); deptFields.setFieldName( 0, "ID" ); deptFields.setFieldType( 1, FieldList.TYPE_STRING ); deptFields.setFieldName( 1, "Name" ); deptFields.setFieldType( 2, FieldList.TYPE_INT ); deptFields.setFieldName( 2, "Manager" ); try { deptRS = RecordStore.openRecordStore( "deptRS", true ); departments = new FieldBasedStore( deptRS, deptFields ); } catch( RecordStoreException e ){ System.out.println( "Could not create deptRS" ); return; } int[] empRecordID; int[] deptRecordID; int i; // Add the data... try { empRecordID = new int[ empList.length ]; for( i = 0; i < empList.length; ++i ){ empRecordID[i] = employees.addRecord( empList[i] ); } deptRecordID = new int[ deptList.length ]; for( i = 0; i < deptList.length; ++i ){ deptRecordID[i] = departments.addRecord( deptList[i] ); } } catch( RecordStoreException e ){ System.out.println( "Error adding record" ); return; } catch( IOException e ){ System.out.println( "Error writing field" ); return; } // Now fetch the data back and print it... System.out.println( "---- Employee data ----" ); for( i = 0; i < empRecordID.length; ++i ){ printRecord( employees, empRecordID[i] ); } System.out.println( "---- Department data ----" ); for( i = 0; i < deptRecordID.length; ++i ){ printRecord( departments, deptRecordID[i] ); } System.out.println( "Closing empRS" ); try { empRS.closeRecordStore(); } catch( RecordStoreException e ){ System.out.println( "Error closing empRS" ); } System.out.println( "Closing deptRS" ); try { deptRS.closeRecordStore(); } catch( RecordStoreException e ){ System.out.println( "Error closing deptRS" ); } System.out.println( "Dumping record stores..." ); // Analyze them... RMSAnalyzer analyzer = new RMSAnalyzer( new RMSAnalyzer.SystemLogger( 10 ) ); analyzer.analyzeAll(); } protected void startApp() throws MIDletStateChangeException { if( display == null ){ display = Display.getDisplay( this ); initMIDlet(); } } public class MainForm extends Form { public MainForm(){ super( "RMSMappings" ); addCommand( exitCommand ); addCommand( testCommand ); setCommandListener( RMSMappings.this ); } } }
正如它们现在所表明的,FieldBasedStore
和FieldList
类可以使用一些改善。例如,基于字段的库也可以基于游标,像一个 JDBC结果集,并使您在记录之间移动并抽取单个字段值。记录库可以缓冲记录。字段列表可以返回给定名称的字段的索引。当然,这些和其他的改进都是有同样代价的,于是您不得不在需要什么和什么会在您的平台上起作用之间进行平衡。当您预先不知道数据结构时,一个通用的基于字段的方法是最合适的。当然,预先不知道数据结构的情况并不常出现。通常,最好的方案是在一个持久存储对象中封装已知的对象类型。我在这里提出通用的方法,主要是告诉您最多能做些什么。
下一部分提要
第四节将讨论使用 RMS 的更高级的方面:遍历记录库和筛选记录。
相关推荐
4. **数据存储**:MIDP2.0提供了Record Management System(RMS),一种简单的数据库系统,用于在设备上持久化数据。 5. **安全和隐私**:MIDP2.0包含了安全模型,如数字签名、权限管理,以保护用户的隐私和设备的...
4. 数据存储(Data Storage):Record Management System(RMS)是MIDP中的一个小型数据库系统,用于在设备上持久化存储数据。开发者可以创建记录存储区,并读写数据。 5. 游戏开发(Game Development):MIDP的...
在J2ME中,游戏开发主要依赖于MIDP和Game API。MIDP定义了应用程序的基本框架,而Game API则提供了更具体的游戏开发功能。通过结合使用这两个API,开发者可以构建从简单的休闲游戏到更复杂的角色扮演游戏等各类游戏...
此外,由于J2ME不支持标准的JDBC驱动,可能需要寻找兼容的库,例如MIDP-DBMS,它提供了简单的API来与SQL数据库交互。 5. 数据库优化: 存储大量图片时,考虑使用数据库的索引优化、分区等技术提高查询性能。另外,...
Midp提供用户界面组件和游戏支持,而CLDC则定义了运行时环境和内存管理。 在手机上通过HTTP查询数据库的过程通常包括以下步骤: 1. **建立HTTP连接**:首先,我们需要创建一个HttpURLConnection对象,这是Java标准...
此外,Record Management System (RMS) 提供了一个数据库接口,用于存储和检索应用程序数据。 三、MIDP项目开发流程 1. 设计阶段:首先,开发者需要明确应用程序的目标、功能和用户体验。在这个阶段,通常会绘制UI...
RMS允许应用程序在设备上存储和检索数据,类似于小型数据库。开发者可以通过RecordStore类来操作RMS。 此外,为了实现跨设备兼容性和优化性能,开发者还需要熟悉MIDP的屏幕尺寸和输入设备的适配策略,以及内存管理...
5. **数据存储**:了解如何使用Record Management System (RMS)存储和检索数据,这是MIDP特有的本地数据库系统。 6. **网络通信**:使用MIDP的Connector类进行HTTP或TCP/IP通信,实现数据的上传和下载。 7. **图形...
JavaMIDP2.0是J2ME(Java 2 Micro Edition)的一部分,提供了比MIDP1.0更强大的功能和API。它引入了用户界面改进,如可定制的轻量级组件,以及对网络和文件系统访问的增强。 **2. 开发环境** 要进行MIDP2.0开发,你...
3. **游戏开发:** 在J2ME环境下进行游戏开发涉及到利用MIDP和其他相关的API来构建可以在移动设备上运行的游戏。 #### 核心概念与技术要点: - **面向对象编程 (OOP):** 游戏开发中广泛使用了面向对象编程的概念...
rd Store 是 J2ME MIDP 平台上的一种轻量级数据库系统,主要用于移动设备上的数据存储。它不是传统意义上的数据库管理系统,而是提供了一种简单的方式来管理和操作数据记录。RMS 提供了基本的数据管理功能,适合小型...
J2ME提供了多种方式来实现数据存储和检索,包括使用MIDP(Mobile Information Device Profile)的WMA(Wireless Messaging API)或JSR-184的M3GPOJO(Mobile 3D Graphics Persistent Object)进行简单的数据管理,...
在J2ME和MIDP环境中,优化数据库连接显得尤为重要,因为移动设备的资源限制更加突出。使用连接池和PreparedStatement等技术可以有效地减少资源消耗,提高响应速度,确保应用程序在有限连接设备上的高效运行。 总之...
### 基于Socket的手机与数据库服务器通信的研究 #### J2ME与Socket通信技术概述 ...此外,结合定时机制和数据库技术的应用,进一步提高了系统的可靠性和实用性。这对于开发移动应用来说具有重要的参考价值和实际意义。
WTK是用来开发MIDP的,为了让MIDlet可以顺利编译和执行,WTK必须具有CLDC和MIDP的类库,WTK可以帮助我们省去额外安装调试这些类库的时间。而不同版本的WTK包含的程序库内容是不一样的,比如说2.0中包含了midpapi....