Data Storage

Overview

The data storage category contains data storage packages.

You can choose from a variety of approaches for storing, sharing, and managing your application data. This document is designed to help you choose which data storage option is best suited for your application and to help you get started implementing your data storage solution. It provides an overview of data storage options; describes each option in more detail; and then introduces some data management tools.

This document includes the following sections:


For more information about data storage, see the Data Storage Development Guide.

Introduction to data storage [back to top]

Different BlackBerry® devices support different types of memory. The following types of memory are possible on BlackBerry devices:

When you consider where to store essential data, keep in mind that microSD cards can be removed. Data can be corrupted if the card is removed while data is being written. For both external media card storage and built-in media storage, data can be corrupted if the battery is removed from the device while data is being written. Application storage is more robust in these situations.

There is more latency in writing to application storage than there is in reading from it. For example, reading from the persistent store is relatively fast while commits are relatively slow.

Data Storage Options

The following table lists data storage options across the top, and compares some main features.

Features File System SQLite® Persistent Store MIDP RMS Runtime Store
Data format that can be stored Any; but is most useful for large read-only files Relational database file Java object Serialized Java object
Storage locations Might be possible on application storage, external media card, and built-in media storage Might be possible on external media card and built-in media storage Application storage Application storage Application storage
Maximum limit Size of partitions the user has access to Size of partitions the user has access to Available application storage Differs according to version (see details) Available application storage
Compatibility (BlackBerry Device Software versions) FileConnection API: 4.2 and later 5.0 and later All All 3.6 and later
Persist across device restarts? Yes Yes Yes Yes No
Share data between applications? Yes Yes Yes Yes Yes

Considerations for choosing a data storage option

Storing files in the file system (FileConnection API) [back to top]

Packages: net.rim.device.api.io.file, javax.microedition.io.file

You can programmatically create and manage the files and folders on BlackBerry devices using the FileConnection API. The FileConnection API was introduced with BlackBerry Device Software 4.2.

The FileConnection API is defined by JSR 75 and is built on the Generic Connection Framework (GCF). It is implemented in javax.microedition.io.file.* and its main component is the FileConnection class. Unlike other GCF connections, FileConnection objects can be successfully returned from the Connector.open() method without referencing an existing file or folder. This behavior allows for the creation of new files and folders on a file system.

RIM provides the following extensions to the FileConnection API. They are in the net.rim.device.api.io.file.* package:

There are two types of storage you can access: internal storage and external media card storage.

Internal storage is application storage or built-in media storage. To access internal storage, use the path file:///store. For example,

FileConnection fc = (FileConnection)Connector.open("file:///Store")

You can access external media card storage only on devices with microSD cards. To access external media card storage, use the path file:///SDCard. For example,

FileConnection fc = (FileConnection)Connector.open("file:///SDCard")

Files created by your application are not automatically removed when your application is removed.

When a BlackBerry device is connected to a computer using USB, users can transfer files from external media card storage with Mass Storage mode or the BlackBerry® Desktop Manager.

Devices that have built-in media storage have a file system partition called System. In BlackBerry Device Software 5.0 and later, the system partition is reserved for system use and is read-only. In BlackBerry Device Software versions earlier than 5.0, the system partition is read/write. You can access this partition with the path file:///system.

Click for code sample: Displaying the path to the video folder using System.getProperty()

import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.container.MainScreen;

public class GetVidDir extends UiApplication
{
    public static void main(String args[])
    {
        GetVidDir app = new GetVidDir();
        app.enterEventDispatcher();
    }
    
    public GetVidDir()
    {
        HomeScreen hs = new HomeScreen();
        pushScreen(hs);
    }
}

class HomeScreen extends MainScreen
{
    public HomeScreen()
    {
        LabelField msg = new LabelField(System.getProperty("fileconn.dir.videos"));
        add(msg);
    }
}

Click for code sample: Retrieving a list of mounted roots

import java.util.Enumeration;
import javax.microedition.io.file.FileSystemRegistry;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.container.MainScreen;

public class ListMountedRoots extends UiApplication 
{
   public static void main(String[] args) 
   {
      ListMountedRoots app = new ListMountedRoots();
      app.enterEventDispatcher();
   }
	
   public ListMountedRoots()
   {
      pushScreen(new HomeScreen());
   }
}

class HomeScreen extends MainScreen
{
    public HomeScreen() {
        StringBuffer msg = new StringBuffer( “The mounted roots are:\n”);
        Enumeration e = FileSystemRegistry.listRoots();
        while (e.hasMoreElements()) {
            msg.append( e.nextElement() );
            msg.append( ‘\n’ );
        }
        add(new LabelField(msg));
    }

}

Click for code sample: Creating a file

import net.rim.device.api.system.Application;
import javax.microedition.io.*;
import javax.microedition.io.file.*;
import java.io.IOException;

public class CreateFileApp extends Application 
{
    public static void main(String[] args) 
    {
        CreateFileApp app = new CreateFileApp();
        app.setAcceptEvents(false);
        try 
        {
             FileConnection fc = (FileConnection)Connector.open("file:///store/home/user/newfile.txt");
             // If no exception is thrown, then the URI is valid, but the file may or may not exist.
             if (!fc.exists())
             {
                 fc.create();  // create the file if it doesn't exist
             }
             fc.close();
         }
         catch (IOException ioe) 
         {
            System.out.println(ioe.getMessage() );
         }
    }
}

Click for code sample: Creating a folder

import net.rim.device.api.system.Application;
import javax.microedition.io.*;
import javax.microedition.io.file.*;
import java.io.IOException;

public class CreateFolderApp extends Application 
{
    public static void main(String[] args) 
    {
        CreateFolderApp app = new CreateFolderApp();
        app.setAcceptEvents(false);
        try 
        {    // the final slash in the folder path is required
             FileConnection fc = (FileConnection)Connector.open("file:///SDCard/testfolder/");
             // If no exception is thrown, then the URI is valid, but the folder may or may not exist.
             if (!fc.exists())
             {
                 fc.mkdir();  // create the folder if it doesn't exist
             }
             fc.close();
         }
         catch (IOException ioe) 
         {
            System.out.println(ioe.getMessage() );
         }
    }
}

Click for code sample: Writing text to a file

import net.rim.device.api.system.Application;
import javax.microedition.io.*;
import javax.microedition.io.file.*;
import java.io.IOException;
import java.io.OutputStream;

public class AddFileContent extends Application 
{
    public static void main(String[] args) 
    {
        AddFileContent app = new AddFileContent();
        app.setAcceptEvents(false);
        try 
        {
             FileConnection fc = (FileConnection)Connector.open("file:///store/home/user/newfile.txt");
             // If no exception is thrown, then the URI is valid, but the file may or may not exist.
             if (!fc.exists())
             {
                 fc.create();  // create the file if it doesn't exist
             }
             OutputStream outStream = fc.openOutputStream(); 
             outStream.write("test content".getBytes());
             outStream.close();
             fc.close();
         }
         catch (IOException ioe) 
         {
            System.out.println(ioe.getMessage() );
         }
    }
}

Click for code sample: Reading randomly-accessed sections of a file

import net.rim.device.api.ui.*;
import net.rim.device.api.io.*;
import javax.microedition.io.file.*;
import javax.microedition.io.*;
import java.io.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;

public class RandomFileAccess extends UiApplication
{
      public static void main(String[] args)
      {
         RandomFileAccess app = new RandomFileAccess();
         app.enterEventDispatcher();
      }
      public RandomFileAccess()
      {
         pushScreen(new HomeScreen());
      }
}

class HomeScreen extends MainScreen
{

      public HomeScreen()
      {
              setTitle("Random File Access Sample");
              try 
              {
                 FileConnection fc = (FileConnection)Connector.open("file:///SDCard/test.gif");
                 boolean bFileExists = fc.exists();
                 if (!bFileExists)
                 {
                   Dialog.alert("Cannot find specified GIF file.");
                   System.exit(0);
                 }
                 DataInputStream in = fc.openDataInputStream();
                 byte[] widthBytes = new byte[2];
                 byte[] heightBytes = new byte[2];
                 
                 if ( in instanceof Seekable ) 
                 {
                    ((Seekable) in).setPosition(6);
                    in.read(widthBytes,0,2);
                 
                    ((Seekable) in).setPosition(8);
                    in.read(heightBytes,0,2);
                 } 
                 
                 int widthPixels  = widthBytes[0]  + 256 * widthBytes[1];
                 int heightPixels = heightBytes[0] + 256 * heightBytes[1];
                 
                 add(new LabelField("Width: " + widthPixels + "\nHeight: " + heightPixels));
                 
                 in.close();
                 fc.close();
             }
             catch (IOException ioe) 
             {
                System.out.println( ioe.getMessage() );
             } 
      } 
}

Storing data in SQLite relational databases (Database API) [back to top]

Package: net.rim.device.api.database

The Database API lets you create and use SQLite relational databases. The Database API was introduced with BlackBerry Device Software 5.0.

For more information about the Database API, see the Data Storage Development Guide.

BlackBerry Device Software 7.0 uses SQLite version 3.7.2.

Each SQLite database is stored in a single file. If you specify only the database name as the parameter value to DatabaseFactory.create(), the database file is created on the external media card. The default location for the database file is /SDCard/databases/application_name/. The name of the application that creates the database is included in the path to avoid name collisions. You cannot store SQLite databases in application storage. On devices that support built-in media storage, you can create database files in built-in media storage by specifying the path /store/.

For more information about SQLite, visit http://www.sqlite.org.

Click for code sample: Creating a SQLite database at the root of a media card

import net.rim.device.api.system.Application;
import net.rim.device.api.database.*;
import net.rim.device.api.io.*;

public class CreateDatabase extends Application
{
    public static void main(String[] args)
    {
        CreateDatabase app = new CreateDatabase();
        try
        {
            URI strURI = URI.create("file:///SDCard/test.db"); 
            DatabaseFactory.create(strURI);
        }
        catch ( Exception e ) 
        {         
            System.out.println( e.getMessage() );
        }  
    } 
}

Click for code sample: Adding a table to a SQLite database

import net.rim.device.api.database.Database;
import net.rim.device.api.database.DatabaseFactory;
import net.rim.device.api.database.Statement;
import net.rim.device.api.io.URI;
import net.rim.device.api.system.Application;

public class AddDatabaseTable extends Application 
{
   public static void main(String[] args)
   {
      AddDatabaseTable app = new AddDatabaseTable();
      try
      {
         URI myURI = URI.create("/SDCard/test.db"); 
         Database d = DatabaseFactory.open(myURI);
         Statement st = d.createStatement( "CREATE TABLE 'People' ( " +
                                              "'Name' TEXT, " +
                                              "'Age' INTEGER )" );
         st.prepare();
         st.execute();
         st.close();
        }
        catch ( Exception e ) 
        {         
            System.out.println( e.getMessage() );
        }
   }
}

Click for code sample: Adding content to a SQLite table

import net.rim.device.api.database.Database;
import net.rim.device.api.database.DatabaseFactory;
import net.rim.device.api.database.Statement;
import net.rim.device.api.io.URI;
import net.rim.device.api.system.Application;

public class AddDatabaseTable extends Application 
{
    public static void main(String[] args)
    {
        AddDatabaseTable app = new AddDatabaseTable();
        try
        {
            URI myURI = URI.create("/SDCard/test.db"); 
            Database d = DatabaseFactory.open(myURI);
            Statement st = d.createStatement("INSERT INTO People(Name,Age) " +
                                             "VALUES ('John',37)");
            st.prepare();
            st.execute();
            st.close();
        }
        catch ( Exception e ) 
        {         
            System.out.println( e.getMessage() );
        }
    }
}

Storing objects persistently (PersistentStore API) [back to top]

Package: net.rim.device.api.system

The Persistent Store API lets you save objects to persistent memory. The storage for each application is distinct because it uses 64-bit globally unique identifiers (GUIDs). The objects are retained in memory after a BlackBerry device restarts. The Persistent Store API is provided in all versions of BlackBerry Device Software.

Data is stored as instances of PersistentObject. The contents of PersistentObject can be any object that implements the Persistable interface. In addition, the API allows the implicit persistence of classes; the following data types automatically implement the Persistable interface and so can be stored:

Cleanup

When an application is uninstalled, persistent objects that are defined within the application are automatically deleted. This is because each persistent object has a class type that is defined in the application. When the application is deleted the class type is deleted, so the persistent objects are deleted.

To ensure cleanup of the persistent storage you use, you should always store your instances of your own classes or your own extensions of provided classes.

Restricting access

If you want to permit only specific, authorized applications to access your application data, then you should use the ControlledAccess class in conjunction with key generation and a key-signing procedure. To restrict access to your data:

  1. Wrap the PersistentObject in a ControlledAccess object that is associated with a signing key that was created using the BlackBerry® Signing Authority Tool.
  2. Sign applications that you want to allow access to the protected data using the same signing key that protects the data.

For detailed instructions, see Protect persistent objects from access by unauthorized applications.

Conserving object handles

The persistent store's consumption of object handles can negatively affect performance. You should consider using the grouping mechanism to persist objects and conserve handles. Because it is possible for commits to the persistent store to occur during garbage collection, without an explicit call to commit(), grouping of objects should always occur before calls to setContents() or commit(). See net.rim.device.api.system.ObjectGroup.

Storing data for MIDlets (MIDP RMS API) [back to top]

Package: javax.microedition.rms

The Mobile Information Device Profile (MIDP) specification provides persistent storage for MIDlets. This persistent storage mechanism is called the MIDP Record Management System (MIDP RMS). It is modeled after a simple record-oriented database. MIDP RMS is the MIDP equivalent of the RIM PersistentStore API. MIDP RMS is available on all MIDP devices. While designed for MIDlets, this storage method can also be used in BlackBerry applications.

The RMS API lets you store and retrieve byte arrays. Each byte array is assigned an integer ID that you use later to retrieve the byte array. Retrieval is done by enumerating over the records.

Applications that use the RMS API can either make data private or allow sharing. The RMS API is frequently used to share data between applications.

RMS data that an application saves is automatically deleted when the application is removed. When you upgrade an application that uses MIDP RMS data storage, the data is retained.

Here are the maximum storage sizes for RMS storage:

BlackBerry Device Software version Maximum individual RecordStore size Maximum total RecordStore size (cumulative for all applications)
Earlier than 4.1 64 KB 64 KB
4.1 to 4.5 64 KB Available device memory
4.6 and later 512 KB Available device memory

Click for code sample: Adding a byte array to the RMS store

int authMode = RecordStore.AUTHMODE_ANY;
boolean bWrite = true;

rs = RecordStore.openRecordStore( "rs", true, 
       authMode, bWrite );
  
byte[] pi = new byte[]{ 3, 1, 4, 1, 5, 9 };
int recordID;

recordID = rs.addRecord(pi, 0, pi.length);

Click for code sample: Storing and retrieving data with RMS store

This example uses the Record Management System to store and retrieve high scores for a game. In the example, high scores are stored in separate records, and are sorted when necessary using a RecordEnumeration.

import javax.microedition.rms.*;
import java.io.DataOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.EOFException;

/**
 * A class used for storing and showing game scores.
 */
public class RMSGameScores
    implements RecordFilter, RecordComparator
{
    /*
     * The RecordStore used for storing the game scores.
     */
    private RecordStore recordStore = null;

    /*
     * The player name to use when filtering.
     */
    public static String playerNameFilter = null;

    /*
     * Part of the RecordFilter interface.
     */
    public boolean matches(byte[] candidate)
        throws IllegalArgumentException
    {
        // If no filter set, nothing can match it.
        if (this.playerNameFilter == null) {
            return false;
        }

        ByteArrayInputStream bais = new ByteArrayInputStream(candidate);
        DataInputStream inputStream = new DataInputStream(bais);
        String name = null;

        try {
            int score = inputStream.readInt();
            name = inputStream.readUTF();
        }
        catch (EOFException eofe) {
            System.out.println(eofe);
            eofe.printStackTrace();
        }
        catch (IOException eofe) {
            System.out.println(eofe);
            eofe.printStackTrace();
        }
        return (this.playerNameFilter.equals(name));
    }

    /*
     * Part of the RecordComparator interface.
     */
    public int compare(byte[] rec1, byte[] rec2)
    {
        // Construct DataInputStreams for extracting the scores from
        // the records.
        ByteArrayInputStream bais1 = new ByteArrayInputStream(rec1);
        DataInputStream inputStream1 = new DataInputStream(bais1);
        ByteArrayInputStream bais2 = new ByteArrayInputStream(rec2);
        DataInputStream inputStream2 = new DataInputStream(bais2);
        int score1 = 0;
        int score2 = 0;
        try {
            // Extract the scores.
            score1 = inputStream1.readInt();
            score2 = inputStream2.readInt();
        }
        catch (EOFException eofe) {
            System.out.println(eofe);
            eofe.printStackTrace();
        }
        catch (IOException eofe) {
            System.out.println(eofe);
            eofe.printStackTrace();
        }

        // Sort by score
        if (score1 < score2) {
            return RecordComparator.PRECEDES;
        }
        else if (score1 > score2) {
            return RecordComparator.FOLLOWS;
        }
        else {
            return RecordComparator.EQUIVALENT;
        }
    }

    /**
     * The constructor opens the underlying record store,
     * creating it if necessary.
     */
    public RMSGameScores()
    {
        //
        // Create a new record store for this example
        //
        try {
            recordStore = RecordStore.openRecordStore("scores", true);
        }
        catch (RecordStoreException rse) {
            System.out.println(rse);
            rse.printStackTrace();
        }
    }

    /**
     * Add a new score to the storage.
     *
     * @param score the score to store.
     * @param playerName the name of the play achieving this score.
     */
    public void addScore(int score, String playerName)
    {
        //
        // Each score is stored in a separate record, formatted with
        // the score, followed by the player name.
        //
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream outputStream = new DataOutputStream(baos);
        try {
            // Push the score into a byte array.
            outputStream.writeInt(score);
            // Then push the player name.
            outputStream.writeUTF(playerName);
        }
        catch (IOException ioe) {
            System.out.println(ioe);
            ioe.printStackTrace();
        }

        // Extract the byte array
        byte[] b = baos.toByteArray();
        // Add it to the record store
        try {
            recordStore.addRecord(b, 0, b.length);
        }
        catch (RecordStoreException rse) {
            System.out.println(rse);
            rse.printStackTrace();
        }
    }

    /**
     * A helper method for the printScores methods.
     */
    private void printScoresHelper(RecordEnumeration re)
    {
        try {
            while(re.hasNextElement()) {
                int id = re.nextRecordId();
                ByteArrayInputStream bais = new ByteArrayInputStream(recordStore.getRecord(id));
                DataInputStream inputStream = new DataInputStream(bais);
                try {
                    int score = inputStream.readInt();
                    String playerName = inputStream.readUTF();
                    System.out.println(playerName + " = " + score);
                }
                catch (EOFException eofe) {
                    System.out.println(eofe);
                    eofe.printStackTrace();
                }
            }
        }
        catch (RecordStoreException rse) {
            System.out.println(rse);
            rse.printStackTrace();
        }
        catch (IOException ioe) {
            System.out.println(ioe);
            ioe.printStackTrace();
        }
    }

    /**
     * This method prints all of the scores sorted by game score.
     */
    public void printScores()
    {
        try {
            // Enumerate the records using the comparator implemented
            // above to sort by game score.
            RecordEnumeration re = recordStore.enumerateRecords(null, this,
                                                                true);
            printScoresHelper(re);
        }
        catch (RecordStoreException rse) {
            System.out.println(rse);
            rse.printStackTrace();
        }
    }

    /**
     * This method prints all of the scores for a given player,
     * sorted by game score.
     */
    public void printScores(String playerName)
    {
        try {
            // Enumerate the records using the comparator and filter
            // implemented above to sort by game score.
            RecordEnumeration re = recordStore.enumerateRecords(this, this,
                                                                true);
            printScoresHelper(re);
        }
        catch (RecordStoreException rse) {
            System.out.println(rse);
            rse.printStackTrace();
        }
    }

    public static void main(String[] args)
    {
        RMSGameScores rmsgs = new RMSGameScores();
        rmsgs.addScore(100, "Alice");
        rmsgs.addScore(120, "Bill");
        rmsgs.addScore(80, "Candice");
        rmsgs.addScore(40, "Dean");
        rmsgs.addScore(200, "Ethel");
        rmsgs.addScore(110, "Farnsworth");
        rmsgs.addScore(220, "Farnsworth");
        System.out.println("All scores");
        rmsgs.printScores();
        System.out.println("Farnsworth's scores");
        RMSGameScores.playerNameFilter = "Farnsworth";
        rmsgs.printScores("Farnsworth");
    }
}

Storing objects nonpersistently (RuntimeStore API) [back to top]

Package: net.rim.device.api.system (Class RuntimeStore)

The runtime store provides a central location for applications to store and share information ona BlackBerry device. Data in the runtime store is not saved when the BlackBerry device is restarted. The RuntimeStore API was introduced with BlackBerry Device Software 3.6.

You can use the runtime store to store any object, and you can retrieve the object from a different process or a different application. You can also restrict access to data.

Objects are stored using a key-value pair. When you store an object in the runtime store, you assign the object a unique ID of type long and later use the ID to retrieve the object from the store.

Before you exit your application, remove objects from the runtime store that your applications no longer require.

Warning: You can create a memory allocation problem if you add an object instance to the runtime store and don't remove it. This is a common cause of memory leaks in BlackBerry applications.

Here are some common uses for the runtime store:

Click for code sample: Storing a String in the runtime store

For simplicity, this example does not show how to create the GUID.

import net.rim.device.api.system.Application;
import net.rim.device.api.system.RuntimeStore;

public class RuntimeSet extends Application 
{
   public static void main(String[] args) 
   {
      RuntimeSet app = new RuntimeSet();
      System.exit(0);
   }

   public RuntimeSet()
   {
      RuntimeStore rts = RuntimeStore.getRuntimeStore();
      long ID = 0x60ac754bc0867248L; //just a unique ID - generate any way you want
      rts.put(ID, "Shared Message");
   }
}
   

Click for code sample: Getting a stored String from the runtime store

For simplicity, this example does not show how to create the GUID.

import net.rim.device.api.system.RuntimeStore;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.component.Dialog;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.container.MainScreen;

public class RuntimeGet extends UiApplication 
{
   public static void main(String[] args) 
   {
      RuntimeGet app = new RuntimeGet();
      app.enterEventDispatcher();
   }
 
   public RuntimeGet()
   {
      RuntimeStore rts = RuntimeStore.getRuntimeStore();
      long ID = 0x60ac754bc0867248L; //just a unique ID - generate any way you want
      String msg = (String)rts.get(ID);
      pushScreen(new HomeScreen(msg));
   }

}

class HomeScreen extends MainScreen
{
   public HomeScreen(String msg)
   {
      add(new LabelField(msg));
   }
}
   

Click for code sample: Creating a singleton by using the RuntimeStore API

The following example creates a singleton using the runtime store. In this example, the static variable _instance is initialized to null for each process running on the system, so getInstance() must check the _instance variable each time it is invoked.

For simplicity, this example does not show how to create the GUID.

import net.rim.device.api.system.*;

class MySingleton {
   private static MySingleton _instance;
   private static final long GUID = 0xab4dd61c5d004c18L;

   // constructor
   MySingleton() {}

   public static MySingleton getInstance() {
      if (_instance == null) {
         _instance = (MySingleton)RuntimeStore.getRuntimeStore().get(GUID);
      if (_instance == null) {
         MySingleton singleton = new MySingleton();

         RuntimeStore.getRuntimeStore().put(GUID, singleton);
         _instance = singleton;
         }
      }

      return _instance;

   }
}
   

Setting up data backup (Synchronization API) [back to top]

Package: net.rim.device.api.synchronization

With the Synchronization API, you can create applications that integrate with the BlackBerry® Desktop Manager or BlackBerry® Enterprise Server to back up data from a BlackBerry device.

To enable an application to back up data, implement the following Synchronization interfaces and use the SyncManager class to register your application for synchronization.

Interface Description
SyncConverter

Converts data between the SyncObject format that is required on the BlackBerry device and the serialized format that is required on the computer

SyncCollection

Represents the collection of synchronization objects for an application

SyncObject

Represents an object that can be backed up and restored

To download a sample application that demonstrates how to implement these interfaces, see the SyncDemo, OTASyncDemo, and OTABackupRestoreDemo code samples that are included with the BlackBerry® Java® Development Environment and the BlackBerry® Java® Plug-in for Eclipse®.

To back up and restore a small amount of data such as application configuration options, you do not have to implement all of these interfaces. Instead, you can extend the SyncItem class and implement its abstract methods. The SyncItem class implements the SyncCollection, SyncConverter, and SyncObject interfaces for you. See Backup and restore small amounts of data using SyncItem.

Managing low memory (LowMemory API) [back to top]

Package: net.rim.device.api.lowmemory

BlackBerry devices require a minimum amount of memory to function properly. When available memory on a BlackBerry device falls below an acceptable threshold, the Low Memory Manager (LMM) attempts to provide more available memory resources. The LMM prioritizes objects in memory and marks the less critical objects for deletion by the JVM. Opened messages and older calendar entries are typically deleted first.

You should design your application to work with the LMM to make available as much memory as possible when the device is low on memory resources. To do so, implement the LowMemoryListener interface and register it with the LMM by calling the static LowMemoryManager.addLowMemoryListener() method. The LowMemoryListener interface has a single method, freeStaleObject(), that is invoked by the LMM when it needs to make memory available. When it invokes freeStaleObject(), the LMM passes a priority parameter to indicate that it is initiating a high, medium, or low memory recovery request. Be careful to return true from freeStaleObject() if you freed any resources and false otherwise. This is important because the LMM needs an accurate accounting of the memory freeing progress.

Click for code sample: Implementing the freeStaleObject() method

public boolean freeStaleObject( int priority ) 
{
boolean dataFreed = false;
switch( priority ) 
{
   case LowMemoryListener.HIGH_PRIORITY:
      dataFreed = freeVector( _data._high );
      _priority = LowMemoryListener.LOW_PRIORITY;
      break;
   case LowMemoryListener.MEDIUM_PRIORITY:
      dataFreed = freeVector( _data._medium );
      _priority = LowMemoryListener.HIGH_PRIORITY;
      break;
   case LowMemoryListener.LOW_PRIORITY:
      dataFreed = freeVector( _data._low );
      _priority = LowMemoryListener.MEDIUM_PRIORITY;
      break;
}

if( dataFreed ) 
{
   _persist.commit();
}
return dataFreed;
}
/**
* A private method that frees the priority vector.
* @param vector The vector to free.
* @return A boolean that indicates whether any objects were freed by this
* method.
*/
private boolean freeVector( Vector vector ) 
{
   boolean dataFreed = false;
   int size = vector.size();
   for( int i = size - 1; i >= 0; i-- ) 
   {
      Object obj = vector.elementAt( i );
      vector.removeElementAt( i );
      LowMemoryManager.markAsRecoverable( obj );
      dataFreed = true;
   }
return dataFreed;
}

Removing Sensitive Data (MemoryCleaner API) [back to top]

Package: net.rim.device.api.memorycleaner

The memory cleaner can erase sensitive data that is stored in memory on a BlackBerry device. Specific events trigger it to clear various caches and perform secure garbage collection. The memory cleaner is not on by default. To turn it on, on the device click Options > Security Options > Advanced Security Options > Memory Cleaning and set Status to Enabled. The memory cleaner is turned on automatically when you enable encryption.

Users can configure which events trigger a memory cleaning. You can register your application to be notified if one of those events occurs. To do so, implement the MemoryCleanerListener interface and register it using one of the static methods MemoryCleanerDaemon.addListener() or MemoryCleanerDaemon.addWeakListener(). The interface has two methods, cleanNow() and getDescription(). The cleanNow() method is called by the memory cleaner when a user configurable event occurs. The memory cleaner passes an event parameter when it calls cleanNow() to indicate the event that initiated the memory clean request. The getDescription() method is invoked by the memory cleaner if it must display information about the applications that are registered cleaners. This functionality is required, for example, on the Memory Cleaning option screen.

Click for code sample: Implementing and registering the MemoryCleanerListener interface

import net.rim.device.api.memorycleaner.*;
import net.rim.device.api.system.Application;
import net.rim.device.api.ui.component.Dialog;


public class MemoryCleaner extends Application implements MemoryCleanerListener
{

   public static void main(String[] args) 
   {
      MemoryCleaner app = new MemoryCleaner();
      app.enterEventDispatcher();
   }

   public MemoryCleaner()
   {
      // if you don't use the second parameter to pass true, the cleaner will start immediately
      MemoryCleanerDaemon.addListener(this,false);
   }

   public boolean cleanNow(int event)
   { 
      switch(event)
      {
         case MemoryCleanerListener.EVENT_DEVICE_LOCK:
            // if you free something
            // return true;
            // if you don't free anything
            return false;

         case MemoryCleanerListener.EVENT_IDLE_TIMEOUT:
            //if you free something
            //return true;
            // if you don't free anything
            return false;

            //add additional cases for any events you want to clean in response to
      }
      return false;
   }

   public String getDescription()
   {
      return "Sample Memory Cleaner";
   }
}

Copyright 1999-2011 Research In Motion Limited. 295 Phillip Street, Waterloo, Ontario, Canada, N2L 3W8. All Rights Reserved.
Java is a trademark of Oracle America, Inc. in the US and other countries.


Legal