BlackBerry Object Persistence

The BlackBerry has a simple data persistence model loosely based on Java serialization. You don’t read or write data explicitly, what you do is store it in Java objects and pass those directly to the persistence APIs for storage. It’s very convenient and quite simple to use.

What Can Be Stored?

The object store knows how to store all Java primitives: int, long, etc. In the documentation for the net.rim.device.api.system.PersistentObject class you will also discover that the following classes are implicitly persistable: Boolean, Byte, Character, Hashtable, Integer, Long, Object, Short, String, and Vector. The object store can also store arrays.

Any other class can be made persistable providing it satisfies these conditions:

  • It implements the net.rim.device.api.util.Persistable interface
  • Its instance data (including object references) is itself persistable

The built-in support for persistence of primitives and common classes like String makes it easy to create persistable classes.

Note: Every non-system class that is to be persisted must explicitly implement the Persistable interface, even if a superclass already implements it.

How to Persist Data

A persistable object can be stored using the net.rim.device.system.PersistentStore class by associating it with a unique integer key, which by convention is the 32-bit hash of the fully qualified name of a class in your application. It can be retrieved at any time using that same key. An instance of the net.rim.device.system.PersistentObject class is used as an intermediary. Here’s an example drawn from the API documentation:

 import java.util.*;
 import net.rim.device.api.system.*;
 
 public class AddressBook
 {
     static Vector addresses;
     static PersistentObject persist;
 
     static {
         // Hash of "net.rim.sample.AddressBook".
         long KEY =  0xa3b3159378f59a29L;
         persist = PersistentStore.getPersistentObject( KEY );
         addresses = (Vector) persist.getContents();
         if( addresses == null ) {
             addresses = new Vector();
             persist.setContents( addresses );
             persist.commit()
         }
     }
     
     void add( Address a ) {
         addresses.addElement( a );
         persist.commit();
     }

To load persistent data, you first call PersistentStore.getPersistentObject to obtain the unique instance of PersistentObject associated with the key. The actual data stored by the object is retrieved using getContents. If there is no data, a call to setContents is required to make the linkage. Anytime you change the data in the application, call commit to persist the changes into the store — the data is not saved until you commit it.

Key Generation

The contents of the persistent store are global to the device, so any application can access any other application’s data. There are important security considerations with this, of course, and if this is an issue you should definitely look into how to secure your data when using the persistent store. It’s rather complicated, I’m afraid.

We’re not going to go down that path, though, and instead we’ll assume it’s OK to store unencrypted data in the persistent store.

The main thing is to keep applications from writing over each other’s data. As I mentioned previously, there’s a convention for generating the key used to store the persistent data. Using the BlackBerry JDE (Java Development Environment), type the name of a fully-qualified class into the text editor. Select that name, then right click on it and choose the “Convert …. to long” option in the popup menu. The string will be hashed and converted into a hexadecimal value. It doesn’t guarantee uniqueness, but the chances are pretty good that there will no collisions if you start with a class name that is unique to your own application.

Automatically Deleting Persisted Data

When your application is uninstalled by the user, all persistent data saved by the application is automatically deleted as long as the persistent data references one or more of you application’s classes. A vector of strings would not be deleted, however, because the vector and the strings are system classes. If you want to use a Vector or Hashtable to store your data, the easiest way to ensure that the data is deleted on deinstallation is to create a persistable subclass of Vector/Hashtable within the Java package used by your application.

Class Changes

What if you change the layout of a class that’s been persisted? Unless you’re very careful, the data will be lost. You can add or remove methods and add or remove static data, but adding, removing or reordering instance data will land you in trouble.

The best way to avoid problems is to not change any of your persistable classes. Either ensure that your classes are extensible by design or create separate classes and provide a way to load data stored in the old format (the previous class) into the new format (the new class).

For More Information

To learn more about BlackBerry object persistence, read the API documentation for the following classes:

In the next post I’ll show you a simple class I use for persisting application data.