Configuring Data Storage

Overview

This category includes the APIs that you can use to store data and manage memory on a BlackBerry® device or a connected desktop computer. You can store information in files and folders, create device-side relational databases, store objects, and provide backup and restore functionality. In addition, you can implement data storage options that are specifically designed for BlackBerry devices. You can also leverage runtime storage to share data, that does not need to persist, between applications.

The Memory Management APIs in this category enable your application to respond to low memory conditions and to take precautions when you store sensitive information in memory.

For more detailed information about data storage on BlackBerry devices, see the following sections:

Files and Folders[back to top]

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

You can programmatically create and manage files and folders on a BlackBerry device. BlackBerry devices support different types of persistent storage on which file systems are mounted. Some devices include an embedded multimedia card (eMMC) to which you have read/write access. Others have a smaller amount of internal flash memory that can be used in the same way. Some internal memory is dedicated to system use and is read-only. Devices running BlackBerry® Device Software 4.2.0 or later support optional media card memory.

The following table describes the different file systems and the paths to use to work with them.

File system Description
Internal

This file system is mounted on eMMC memory in devices that have eMMC memory. It is mounted on internal flash memory in other devices. The size of this file system varies according to the device. You can read and write to this file system by using the path file:///store.

System

This file system partition is present only on devices that have eMMC memory. It is reserved for system use and is read-only on devices that run BlackBerry® Device Software 5.0. Putting a device in USB mass storage mode does not impact on the use of this file system. You can read from this file system by using the path file:///system.

Media card

This is the file system on an installed MicroSD card. The size of this file system corresponds to the size of the inserted MicroSD card. You can read from and write to this file system by using the path file:///SDCard.

BlackBerry devices that run BlackBerry® Device Software 4.2 or later implement JSR 75 which provides the APIs that enable you to work with file systems. The API is built upon the Generic Connection Framework (GCF) and its key component is the FileConnection class. There are many web-based resources available to learn more about the JSR 75 API and the GCF.

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

In addition to the standard JSR 75 interfaces that are provided, RIM provides extended types that you can use to perform additional tasks with files and folders.

Click for code sample: Display the path to the video directory by 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: Retrieve 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()
   {
      String msg = "The mounted roots are:\n";
      Enumeration e =  FileSystemRegistry.listRoots();
      while (e.hasMoreElements()) 
      {
         String rootName = (String)e.nextElement();
         msg += rootName + "\n";
      }
      add(new LabelField(msg));
   }
}

Click for code sample: Create 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: Create 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: Write 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: Read 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() );
             } 
      } 
}

SQLite[back to top]

Package: net.rim.device.api.database

You can create a relational database on a BlackBerry device. BlackBerry devices that run BlackBerry® Device Software 5.0 or later include the open source SQLite® library. The SQLite API gives you access to this library. Although the SQLite database is unique in ways that make it ideal for use on a smartphone, the API is similar to other database APIs.

Use the following approach to work with an existing SQLite database.

SQLite databases created by your application are not removed automatically when your application is removed.

For more information about SQLite, visit http://www.sqlite.org. For more information about the SQLite implementation and SQLite API on BlackBerry devices, read the SQLite Development Guide.

Click for code sample: Create 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: Add 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: Add 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() );
        }
    }
}

Persistent Storage[back to top]

Package: net.rim.device.api.system

The Persistent Storage API enables you to save objects directly to persistent memory. You can use the API to store information that you want to persist across reboots. You can protect the stored information with your code signing key. For more information about the Persistent Storage API, see the related topics in the BlackBerry Java Applications Core Development Guide.

If the objects you store are contained in a package that is unique to your application, then those objects are deleted when your application is removed. In fact, if you store a Vector that contains at least one object in a package unique to your application, the entire Vector will be deleted when your application is removed. To ensure clean up of the persistent storage you use, you should always store your instances of your own classes or your own extensions of provided classes.

Click for code sample: Read and write data by using the Persistent Storage API

import net.rim.device.api.system.*;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.util.Persistable;


public class SimplePersistence extends UiApplication implements FieldChangeListener 
{
   private LabelField labelMsg;
   private ButtonField fieldSetMsg1;
   private ButtonField fieldSetMsg2;
   
   private static PersistentObject store;
   private SecretMessage msg;

   public static void main(String[] args) 
   {
      SimplePersistence app = new SimplePersistence();
      app.enterEventDispatcher();
   }
   
   public SimplePersistence() 
   {
      MainScreen ms = new MainScreen();
      fieldSetMsg1 = new ButtonField("Message 1",ButtonField.CONSUME_CLICK);
      fieldSetMsg2 = new ButtonField("Message 2",ButtonField.CONSUME_CLICK);
      
      fieldSetMsg1.setChangeListener(this);
      fieldSetMsg2.setChangeListener(this);

      ms.add(fieldSetMsg1);
      ms.add(fieldSetMsg2);


      store = PersistentStore.getPersistentObject(0xe358b952f5f3b797L);
      msg = getSecretMessage();
      
      labelMsg = new LabelField(msg.getMessage());
      ms.add(labelMsg);
      this.pushScreen(ms);
   }
   public void fieldChanged(Field field, int context){
      if(field == fieldSetMsg1) 
      {
         labelMsg.setText("The 1st message.");
         saveSecretMessage();
      }
      else if(field == fieldSetMsg2) 
      {
         labelMsg.setText("The 2nd message.");
         saveSecretMessage();
      }
   }

   public void saveSecretMessage(){
       synchronized(store) {
          msg.setMessage(labelMsg.getText());
          store.setContents(msg); 
          store.commit();
       }
   }
   public SecretMessage getSecretMessage() {
      SecretMessage msg = new SecretMessage();
      synchronized(store) 
      {
         msg = (SecretMessage)store.getContents();
         if(msg == null) 
         {
            msg = new SecretMessage();
         }
      }
      return msg;
   }  
}

class SecretMessage implements Persistable 
{
   private String msg;
   public String getMessage() 
   {
      return msg;
   }
   public void setMessage(String message) 
   {
      msg = message;
   }
   
   public SecretMessage()
   {
     msg = "Message not yet set.";
   }
}
   

Runtime Storage[back to top]

Package: net.rim.device.api.system

You can share runtime objects between BlackBerry device applications that run on the same BlackBerry device. BlackBerry devices provide a central runtime store for this purpose. Data in the runtime store is not saved when the BlackBerry device is restarted. When you store an object in the runtime store, you assign the object a unique, long ID. You use the ID to retrieve the object from the store.

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

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: Get a stored String from the runtime store

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));
   }
}
   

MIDP RMS Storage[back to top]

BlackBerry devices support the MIDP Record Management System (RMS) as a mechanism for storing persistent data. It lets you store and retrieve byte arrays, each of which gets assigned an integer tag that you use to later retrieve it.

On a BlackBerry device running BlackBerry® Device Software earlier than 4.1, RMS data saved by an application is not automatically removed when the application is deleted. On a BlackBerry device running BlackBerry Device Software later than 4.1, RMS data saved by an application is automatically removed when the application is deleted. When you upgrade a program that uses RMS data storage, the data is preserved.

Click here for a code sample showing how to add a byte array to the RMS store.

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

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

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

Synchronization[back to top]

The Synchronization API enables you to create applications that integrate with the BlackBerry® Desktop Manager to back up data to a file on a connected desktop computer and restore it later to a BlackBerry device.

Data Backup

The BlackBerry Desktop Manager provides a Backup and Restore tool that enables a user to save BlackBerry device data to a file on their desktop computer, and later use that file to restore the data to the BlackBerry device. To enable your application to make use of this service, you must implement three interfaces that are provided in the Synchronization API 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 desktop

SyncCollection

represents the collection of synchronization objects for an application

SyncObject

represents an object that can be backed up and restored to the user computer

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

To back up and restore a very 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. This KB article provides sample code that demonstrates that approach.

Memory Management[back to top]

Low Memory Manager

Package: net.rim.device.api.lowmemory

BlackBerry devices require a minimum amount of free memory available to function properly. The Low Memory Manager (LMM) handles memory resources on a BlackBerry device when available memory resources fall below the acceptable threshold. The LMM attempts to provide more available memory. The LMM frees memory resources by prioritizing objects in memory, and marking objects that it deems less critical for deletion by the virtual machine. Opened messages and older calendar entries are typically deleted first.

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

Click for code sample: Implementation of 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;
}

Memory Cleaner

Package: net.rim.device.api.memorycleaner

The memory cleaner (MC) 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 MC is not on by default. To turn it on, click Options > Security Options > Advanced Security Options > Memory Cleaning and set Status to Enabled. Enabling encryption Options > Security Options > Encryption turns on MC automatically.

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, you have to 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 MC when any of the user configurable events occur. The MC passes an event parameter when it calls cleanNow() to indicate the event that initiated the memory clean request. The getDescription() method is invoked by MC 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: Implement and register 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-2009 Research In Motion Limited. 295 Phillip Street, Waterloo, Ontario, Canada, N2L 3W8. All Rights Reserved.
Copyright 1993-2003 Sun Microsystems, Inc. 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
Copyright 2002-2003 Nokia Corporation All Rights Reserved.
Java is a trademark of Sun Microsystems, Inc.