Home arrow Java arrow Storing and Retrieving Data
JAVA

Storing and Retrieving Data


Storing bytes of data locally on a device equipped for the Mobile Internet Device Profile (MIDP) is easy. MIDP allows you to store arrays of bytes. But what if the data you need to store isn't in the form of bytes? And how can you make the data small enough so that storing it on a device with a relatively small amount of memory is not a problem? That's where this article comes in. It is excerpted from the book J2ME Games with MIDP2, written by Carol Hamer (Apress, 2004; ISBN: 1590593820).

Author Info:
By: Apress Publishing
Rating: 4 stars4 stars4 stars4 stars4 stars / 24
May 12, 2005
TABLE OF CONTENTS:
  1. · Storing and Retrieving Data
  2. · Serializing More Complex Data Using Streams
  3. · Using Data Types and Byte Arithmetic
  4. · Applying Data Storage to a Game
  5. · Converting an array of bytes into a dungeon
  6. · Creating the Complete Example Game
  7. · DungeonManager.java
  8. · Doors and keys

print this article
SEARCH DEVARTICLES

Storing and Retrieving Data
(Page 1 of 8 )

IT TURNS OUT THAT storing bytes of data locally on a device thatís equipped for the Mobile Internet Device Profile (MIDP) is easy. Therefore, this chapter starts with an extremely simple example: Youíll take the maze game example from Chapter 3 and store the userís preferred size information. Then, each time the user restarts the game, the game will automatically create the maze with walls of the userís chosen width rather than starting at the default width.

The hard part of data storage and retrieval is when you have more complicated data to store. MIDP allows you to store arrays of bytes only. But what if the data you want to store isnít in the form of bytes? The simplest thing to do is to use the classes java.io.DataInputStream and java.io.DataOutputStream to convert other types of data to bytes. But since memory can be scarce on a small device, itís a good idea to understand how you convert integers to bytes, and vice versa, so you can compact your data to store it more efficiently. Therefore, in this chapter, youíll see a utility class that converts ints to bytes and back again, compacting the data appropriately if it falls within a certain size range. Then youíll see a complete game example (using the utility class) in which the user can save a game thatís currently in play and start it again later from that point. This example illustrates how to store complex data in a real game situation.

Saving Simple Data

The MIDP Record Management System (RMS) is simple. Its package javax.microedition.rms has only one class: RecordStore. A RecordStore is a collection of records, which are in fact just byte arrays. A RecordStore is identified by the property MIDlet-Vendor and the property MIDlet-Name in the jad file, as well as by the name given to the RecordStore by the MIDlet that created it. This means that within a MIDlet suite (a group of MIDlets in the same jar file), the MIDlets share the RecordStores theyíve created, but MIDlet suites donít share RecordStores with other MIDlet suites (unless theyíre explicitly given permission to do so; youíll learn more about that in the sidebar ďUsing Secure Connections While Selling Your GameĒ in Chapter 7). A RecordStore is identified by the MIDlet-Vendor and the MIDlet-Name properties in addition to the storeís name, so you donít have to start your RecordStoreís name with your own package name to keep it in a separate namespace from the RecordStores of other unrelated MIDlets. Itís a good thing, too, because the name of the RecordStore can be only 32 (case-sensitive) characters long, so you donít want to waste too many of them.

To create a RecordStore, all you have to do is call the static method RecordStore.openRecordStore() with the name of the RecordStore you want to create and the value true as arguments. The second argument true answers the question of whether to create the RecordStore if it doesnít already exist. Once you have a handle to a RecordStore (either by creating it or by opening an existing RecordStore), you can get or set the records. Note that a record isnít a separate class; itís merely a byte array. The RMS assigns each record an integer record ID that you use to get or replace the data array using getRecord() or setRecord(). The first record is assigned the ID of one, and the record IDs go up incrementally from there. If you donít like hard-coding numerical constants into your code (a reasonable inhibition), you can call enumerateRecords to get a RecordEnumerator to help you. The RecordEnumerator wonít necessarily give you the records in the same order theyíd appear in if you had gotten them by number using getRecord(). If youíd like to traverse the records in a particular order, you can create a RecordFilter and/or a RecordComparator, which allows you to define, respectively, which subset of the records will be returned and in what order to return them. Both RecordFilter and RecordComparator are interfaces you must implement yourself if youíd like to use them. These interfaces were obviously designed with address bookĖtype applications in mind rather than games, but you may find a use for them.

In this first example, youíll create the simplest possible RecordStore. Itíll contain only one record, and that record will contain only 1 byte. The example works as follows: You start with the maze game from Chapter 3. After the user selects the preferred width for the maze walls and presses Done, the game calls the new class (PrefsStorage) with the preferred size information, and the PrefsStorage class then saves that information in a RecordStore. The game also consults the PrefsStorage class when the user first opens the game to check for a stored size preference to use when building the maze. If no preferred size has been stored, the PrefsStorage returns the default value.

In addition to adding the PrefsStorage class listed next, you need to modify a few other classes a bit. In the class MazeCanvas, you need to replace the following line:

mySquareSize = 5;

with the following line:

mySquareSize = PrefsStorage.getSquareSize();

Next, in the class SelectScreen, you need to add the following line:

PrefsStorage.setSquareSize(myWidthGauge.getValue());

to the method commandAction, as follows:

public void commandAction(Command c, Displayable s) {
  if(c == myExitCommand){
    PrefsStorage.setSquareSize(myWidthGauge.getValue());  
    myCanvas.newMaze();
  }
}

Aside from those changes, the code for this example is identical to the code of the maze example from Chapter 3.

Listing 5-1 shows the code for PrefsStorage.java.

Listing 5-1.PrefsStorage.java

package net.frog_parrot.maze;
import javax.microedition.rms.*;
/**
  * This class helps to store and retrieve the data about
  * the maze size preferences.
  *
 
* This is a utility class that does not contain instance data,
  * so to simplify access, all the methods are static.
  *
  * @author Carol Hamer
  */
public class PrefsStorage {
  //-------------------------------------------------------  //  static fields
  /**
 
* The name of the datastore.
 
*/
public static final String STORE = "SizePrefs";
//-------------------------------------------------------- //  business methods
/**
 
* This gets the preferred square size from the stored data.
  */
static int getSquareSize() {
  // if data retrieval fails, the default value is 5
  int retVal = 5;
  RecordStore store = null;
  try {
   
// if the record store does not yet exist, the second
    // arg "true" tells it to create.
    store = RecordStore.openRecordStore(STORE, true);
    int numRecords = store.getNumRecords();
    if(numRecords > 0) {
      // the first record has id number 1
      // (In fact this program stores only one record)
      byte[] rec = store.getRecord(1);
      retVal = rec[0];
    }
 
} catch(Exception e) {
    // data storage is not critical for this game and you're
    // not creating a log, so if data retrieval fails, you
    // just skip it and move on.
 
} finally {
    try {
      store.closeRecordStore();
    } catch(Exception e) {
      // if the record store is open, this shouldn't throw.
      }
    }
    return(retVal);
  }
 
/**
  * This saves the preferred square size.
  */
static void setSquareSize(int size) {
  RecordStore store = null;
  try {
   
// since you're storing the int as a single byte,
    // it's important that its value be less than
    // 128. In fact, in real life the value would never
    // get anywhere near this high, but I'm adding this
    // little size check as a last line of defense against
    // errors:
    if(size > 127) {
     
size = 127;
    }
    // if the record store doesn't yet exist, the second
    // arg "true" tells it to create.
    store = RecordStore.openRecordStore(STORE, true);
    byte[] record = new byte[1];
    record[0] = (new Integer(size)).byteValue();
    int numRecords = store.getNumRecords();
    if(numRecords > 0) {
     
store.setRecord(1, record, 0, 1);
    } else {
      store.addRecord(record, 0, 1);
    }
  } catch(Exception e) {
    // data storage isn't critical for this game and you're
    // not creating a log, so if data storage fails, you
    // just skip it and move on.
 
} finally {
    try {
      store.closeRecordStore();
    } catch(Exception e) {
      // if the record store is open, this shouldn't throw.
    }
  }
 }
}

I need to mention one last point before youíre done with this little exampleónamely, the technique for converting back and forth between int values and byte values. The value Iím saving is an int, yet I save it in the form of a byte with little regard for the fact that an int and a byte in Java arenít the same thing at all. (In particular, an int occupies 4 bytes of memory!) Yet youíll notice that in the method setSquareSize() I get a byte value for the argument size merely by calling byteValue(), and in the other direction (in the method getSquareSize()) I convert the byte rec[0] to an int without any sort of conversion operation at all. Whatís going on here? The answer is, I know the argument size is between -128 and 127 in value, so I know it can be stored as a single byte without losing any data. This simple conversion between ints and bytes is useful, but only if youíre 100 percent certain your int isnít going to fall outside the appropriate range. If youíd like to store your int value with more precision, you can use alternate conversion techniques, discussed in the next section.


blog comments powered by Disqus
JAVA ARTICLES

- Java Too Insecure, Says Microsoft Researcher
- Google Beats Oracle in Java Ruling
- Deploying Multiple Java Applets as One
- Deploying Java Applets
- Understanding Deployment Frameworks
- Database Programming in Java Using JDBC
- Extension Interfaces and SAX
- Entities, Handlers and SAX
- Advanced SAX
- Conversions and Java Print Streams
- Formatters and Java Print Streams
- Java Print Streams
- Wildcards, Arrays, and Generics in Java
- Wildcards and Generic Methods in Java
- Finishing the Project: Java Web Development ...

Watch our Tech Videos 
Dev Articles Forums 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Contact Us 
Site Map 
Privacy Policy 
Support 

Developer Shed Affiliates

 




© 2003-2014 by Developer Shed. All rights reserved. DS Cluster - Follow our Sitemap
Popular Web Development Topics
All Web Development Tutorials