blackberry java sdk development guide 1232721 0730084309 001 6.0 us

72
BlackBerry Java SDK Data Storage Version: 6.0 Development Guide

Upload: grace-de-la-cruz

Post on 03-Mar-2015

81 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

BlackBerry Java SDKData StorageVersion: 6.0

Development Guide

Page 2: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

Published: 2011-06-09SWD-1701186-0609044131-001

Page 3: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

Contents1 Data storage overview...................................................................................................................................... 4

Data storage features....................................................................................................................................... 4Considerations for choosing a data storage approach.............................................................................. 5

Storage locations.............................................................................................................................................. 5Access to memory..................................................................................................................................... 5

2 Storing files in the file system........................................................................................................................... 7Code sample: Creating a folder......................................................................................................................... 7Code sample: Creating a file............................................................................................................................. 8Code sample: Writing text to a file................................................................................................................... 9Code sample: Reading sections of a binary file................................................................................................ 9Code sample: Displaying the path to the video folder using System.getProperty()......................................... 11Code sample: Retrieving a list of mounted roots............................................................................................. 11

3 Storing data in SQLite databases...................................................................................................................... 13Viewing SQLite databases................................................................................................................................. 13

Simulate a media card............................................................................................................................... 13Security of SQLite databases............................................................................................................................. 13

Code sample: Creating an encrypted SQLite database.............................................................................. 15Performance of SQLite databases..................................................................................................................... 16

Best practice: Optimizing SQLite database performance.......................................................................... 16Creating and deleting SQLite databases........................................................................................................... 18

SQLite database files................................................................................................................................. 18Character encoding.................................................................................................................................... 18Create an SQLite database........................................................................................................................ 19Code sample: Creating an SQLite database............................................................................................... 20Code sample: Adding a schema to an SQLite database............................................................................. 21Code sample: Deleting an SQLite database............................................................................................... 22

Working with SQLite databases........................................................................................................................ 23Using transactions..................................................................................................................................... 23Using SQL parameters............................................................................................................................... 25Using foreign key constraints.................................................................................................................... 28Code sample: Inserting table data............................................................................................................. 29Code sample: Retrieving table data........................................................................................................... 30Code sample: Deleting table data.............................................................................................................. 31Code sample: Updating table data............................................................................................................ 32Code sample: Listing database tables........................................................................................................ 33

Page 4: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

SQLite sample application................................................................................................................................ 34Overview.................................................................................................................................................... 34Files in the sample application.................................................................................................................. 35Featured interfaces................................................................................................................................... 35Featured classes........................................................................................................................................ 36Install the sample application.................................................................................................................... 36Run the sample application....................................................................................................................... 37

4 Storing objects persistently.............................................................................................................................. 38Security of persistent objects........................................................................................................................... 38

Restricting access to persistent objects..................................................................................................... 39Performance of the persistent store................................................................................................................ 42

Best practice: Using efficient data structure selection.............................................................................. 42Best practice: Conserving object handles.................................................................................................. 42Cleanup of persistent objects.................................................................................................................... 43

Creating a persistent store............................................................................................................................... 43Create a persistent data store................................................................................................................... 43Store persistent data................................................................................................................................. 44Store an object in a batch transaction....................................................................................................... 44

Working with the persistent store.................................................................................................................... 44Retrieve persistent data............................................................................................................................ 44Retrieve a collection from persistent storage........................................................................................... 45

5 Storing objects nonpersistently........................................................................................................................ 47Common uses of the runtime store.................................................................................................................. 47Security of the runtime store............................................................................................................................ 47

Restrict access to runtime store data using code signing keys.................................................................. 47Add an object to the runtime store.................................................................................................................. 48Replace an object in the runtime store............................................................................................................ 48Retrieve the runtime store............................................................................................................................... 48Retrieve a registered runtime object................................................................................................................ 49Retrieve an unregistered runtime object......................................................................................................... 49Code sample: Storing a String in the runtime store.......................................................................................... 49Code sample: Getting a stored String from the runtime store......................................................................... 50Code sample: Creating a singleton using the RuntimeStore API...................................................................... 51

6 Storing data in the record store........................................................................................................................ 52Create a record store........................................................................................................................................ 52Add a record to a record store.......................................................................................................................... 52

Page 5: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

Code sample: Adding a record to the record store................................................................................... 53Retrieve a record from a record store.............................................................................................................. 53Retrieve all records from a record store........................................................................................................... 53Code sample: Storing and retrieving data with the record store..................................................................... 54

7 Managing data.................................................................................................................................................. 59Best practice: Minimizing memory use............................................................................................................. 59Removing sensitive data................................................................................................................................... 59Using the Garbage Collector............................................................................................................................. 60

Full garbage collection on a BlackBerry device.......................................................................................... 60Managing low memory..................................................................................................................................... 61

Identifying low memory availability on a BlackBerry device..................................................................... 61Backing up data................................................................................................................................................. 61

8 Find more information...................................................................................................................................... 63

9 Provide feedback.............................................................................................................................................. 64

10 Glossary............................................................................................................................................................ 65

11 Document revision history................................................................................................................................ 67

12 Legal notice....................................................................................................................................................... 68

Page 6: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

Data storage overview 1There are several ways you can store, share, and manage data for your BlackBerry® Java® applications:

Data storage approach Description and API

File system Store data in files and folders using the FileConnection API.SQLite® database Store data in relational databases using the Database API.Persistent store Save objects across device restarts using the PersistentStore API.Runtime store Save objects nonpersistently, which is useful for sharing data between

applications and creating system-wide singletons, using the RuntimeStore API.Record store Store data in the MIDP Record Management System using the RMS API.

Data storage featuresThe following table compares each approach.

Features File system SQLite database

Persistent store Runtime store Record store

Data format Any Relational database file

Java® object Java object Serialized

Storage locations Application storage, external media card, built-in media storage

External media card, built-in media storage

Application storage

Application storage

Application storage

Maximum storage limit

Size of partitions the user has access to

Size of partitions the user has access to

Available application storage

Available application storage

Differs according to BlackBerry® Device Software version

BlackBerry Device Software support

4.2 or later (FileConnection API)

5.0 or later All 3.6 or later All

Persists across device restarts

Yes Yes Yes No Yes

Applications can share data

Yes Yes Yes Yes Yes

Development Guide Data storage overview

4

Page 7: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

Considerations for choosing a data storage approach• The file system is typically the most efficient storage location for large, read-only files such as videos or large

graphics.• For storing data other than large, read-only files, SQLite® databases are a scalable data storage option.• Memory on wireless devices can be very limited, so you should consider not storing all data on the device.

BlackBerry® devices are frequently connected so your application can access data when needed. In many cases, the best approach is to store data across device restarts only for data that is frequently accessed.

• When you consider where to store essential data, keep in mind that microSD cards can be removed.• 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.• The file system and record store are standards-based approaches, while the persistent store and runtime store

are specific to BlackBerry devices. If you want your application to run on other Java® ME compatible devices, you should consider a standards-based approach.

Storage locationsDifferent BlackBerry® devices support different places to store data. The following storage locations are available, depending on the BlackBerry device model:

Application storage This storage location is internal to the BlackBerry device. It contains the operating system, the BlackBerry® Java® Virtual Machine, and an internal file system. Application storage is also called flash memory and on-board memory. Application storage is the only place on a BlackBerry device from which applications can be run. All BlackBerry devices have application storage.

External media card storage

This storage location is a microSD card that BlackBerry device users can insert to extend the amount of storage on their devices. It is optional and removable. A FAT file system is mounted on the media card. MicroSD cards are supported on all devices running BlackBerry® Device Software 4.2 or later, with the exception of the BlackBerry® 8700 Series.

Built-in media storage This storage location is an embedded multimedia card called eMMC. It is not removable. A FAT file system is mounted on the built-in media card. Built-in media storage is also called internal media memory and on-board device memory. Built-in media storage is included on some BlackBerry device models.

Access to memoryThe BlackBerry® Java® environment is designed to prevent applications from causing problems accidentally or maliciously in other applications or on the BlackBerry device. Applications can write only to the BlackBerry device memory that the BlackBerry® Java® Virtual Machine uses; they cannot access the virtual memory or the persistent storage of other applications (unless they are specifically granted access to do so). Custom applications can only

Development Guide Storage locations

5

Page 8: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

access persistent storage or user data, or communicate with other applications, through specific APIs. Research In Motion must digitally sign applications that use certain BlackBerry APIs to provide an audit trail of applications that use sensitive APIs.

Development Guide Storage locations

6

Page 9: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

Storing files in the file system 2You can programmatically create and manage the files and folders on BlackBerry® devices with the FileConnection API. The FileConnection API was introduced with BlackBerry® Device Software 4.2.

The FileConnection API is implemented in the javax.microedition.io.file package.

The FileConnection API is defined by JSR 75 and is built on the Generic Connection Framework. The main component of the FileConnection API is the javax.microedition.io.file.FileConnection class. Unlike other Generic Connection Framework connections, FileConnection objects can be successfully returned from the javax.microedition.io.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. In addition to RIM documentation, there are many sources of information about JSR 75 and the Generic Connection Framework.

In addition, RIM provides extensions to the FileConnection API. The net.rim.device.api.io.file package includes the following class and interfaces:

• FileSystemJournal and FileSystemJournalListener provide a way to detect changes to the file system.

• ExtendedFileConnection allows the encryption and protection of files.

You can access the file system on internal storage and external media card storage:

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

FileConnection fc = (FileConnection)Connector.open("file:///Store")External storage 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 deleted when your application is uninstalled.

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.

Code sample: Creating a folderimport 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) {

Development Guide Storing files in the file system

7

Page 10: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

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, the URI is valid but the folder 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() ); } }}

Code sample: Creating a fileimport javax.microedition.io.*;import java.io.IOException;import javax.microedition.io.file.*;import net.rim.device.api.system.Application.*;

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

Development Guide Code sample: Creating a file

8

Page 11: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

Code sample: Writing text to a fileimport 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() ); } }}

Code sample: Reading sections of a binary fileThis code sample demonstrates how to read sections of a binary file by reading header information from a .gif file. The application reads the width and height of the image from the header. To run the code sample you must place a .gif file in the root folder of a media card in a BlackBerry® device.

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

Development Guide Code sample: Writing text to a file

9

Page 12: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

{ 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) { ioe.printStackTrace(); } } }

Development Guide Code sample: Reading sections of a binary file

10

Page 13: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

Code sample: Displaying the path to the video folder using System.getProperty()import net.rim.device.api.ui.component.LabelField.*;import net.rim.device.api.ui.container.MainScreen.*;import net.rim.device.api.ui.UiApplication.*;

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

Code sample: Retrieving a list of mounted rootsimport java.util.Enumeration.*;import javax.microedition.io.file.FileSystemRegistry,*;import net.rim.device.api.ui.component.LabelField.*;import net.rim.device.api.ui.container.MainScreen.*;import net.rim.device.api.ui.UiApplication.*;

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

Development Guide Code sample: Displaying the path to the video folder using System.getProperty()

11

Page 14: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

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

}

Development Guide Code sample: Retrieving a list of mounted roots

12

Page 15: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

Storing data in SQLite databases 3SQLite® databases require no configuration or administration. Other than schema and data, the database footprint is very small (around 300 KB).

To create and use SQLite databases in a Java application, you must use the Database API. The classes required for SQLite databases are in the net.rim.device.api.database package.

BlackBerry Device Software 6.0 uses SQLite version 3.6.21.

Note: This guide describes how to use SQLite databases in Java® applications. There are other ways to use SQLite databases on a BlackBerry device. They are BlackBerry® WebWorks™ applications, HTML5, and Google® Gears™. For more information, see docs.blackberry.com.

Viewing SQLite databasesSQLite® database viewers are available from third-party vendors. These viewers can be useful aids to your database development process. Database viewers are especially useful for viewing changes to a database. When you run an SQL statement, you can see the result in the database viewer immediately.

An SQLite database viewer runs on your computer, not on the BlackBerry® device. To use the viewer, configure the BlackBerry Smartphone Simulator to emulate a microSD card. When you run your application, your database viewer reads the database from a folder on your computer.

SQLite database viewers cannot work on encrypted databases. You can encrypt the database after you complete your SQLite application.

Simulate a media cardTo view SQLite® databases in a database viewer, you might have to configure the BlackBerry® Smartphone Simulator to emulate a media card. By default, database files are stored on a media card.

1. Create a folder on your computer to store emulation files for the media card.

2. On the Simulate menu, click Change SD Card.

3. Click Add Directory.

4. Navigate to and click the folder you created.

5. Click OK.

6. Click Close.

Security of SQLite databasesYour SQLite® database can have the following security settings:

• Not encrypted, accessible from any application on the BlackBerry® device• Encrypted, accessible from any application on the device

Development Guide Storing data in SQLite databases

13

Page 16: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

• Encrypted and protected, accessible only from applications on the device that are signed with the code signing key

There is no way to create a non-encrypted database and restrict its usage to only one application. That is because there are other ways (using file I/O operations) to read a non-encrypted database file from other applications.

You implement both encryption and protection with the DatabaseSecurityOptions class.

Encryption

The algorithm used to implement SQLite encryption is AES 256.

An encrypted database cannot be moved to another device: it can be opened only on the device where it was originally created. To transfer an encrypted database to another device, you must first decrypt it.

An application can open or create an encrypted database only when the device is unlocked. If a database is open when a device is locked, the database continues to be readable and writable.

Encryption does not protect your database from being accessible to other applications on the device. To restrict access, you must sign the database with a code signing key.

The following code sample creates a database that is encrypted but not signed. It creates a DatabaseSecurityOptions object called dbso that passes true as the single parameter value:

try{ URI myURI = URI.create("file:///SDCard/Databases/SQLite_Guide/" + "MyEncryptedDatabase.db"); DatabaseSecurityOptions dbso = new DatabaseSecurityOptions(true); d = DatabaseFactory.create(myURI,dbso); d.close();}catch ( Exception e ){ System.out.println( e.getMessage() ); e.printStackTrace();}

Encryption and protection

If you want to restrict a database so that it can be accessed only by the application it is a part of, you should sign the database with a code signing key. To restrict access to one application, you should use a unique key that you generate using the Signing Authority tool. This signing is separate from the code signing you do for controlled APIs.

You can also use the code signing key to share access to the database with other specific applications. When multiple applications are signed with the same key, they all have access to the database.

To specify that a database is encrypted and signed, you have a choice of two identical constructors. The following code sample encrypts and protects an existing database. First, the code sample retrieves the code signing key from a file called XYZ. It then encrypts and signs the database. If the database is already encrypted, the method exits gracefully.

Development Guide Security of SQLite databases

14

Page 17: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

CodeSigningKey codeSigningKey = CodeSigningKey.get(CodeModuleManager.getModuleHandle( "SQLiteDemo" ), "XYZ"); try { DatabaseFactory.encrypt(uri, new DatabaseSecurityOptions(codeSigningKey)); } catch(DatabaseException dbe) { errorDialog("Encryption failed - " + dbe.toString()); }

Code sample: Creating an encrypted SQLite databaseBy default, database files are stored on a media card. If you are using a BlackBerry® Smartphone Simulator, you might need to Simulate a media card.

import net.rim.device.api.ui.*;import net.rim.device.api.ui.component.*;import net.rim.device.api.ui.container.*;import net.rim.device.api.database.*;import net.rim.device.api.io.*;

public class CreateEncryptedDatabase extends UiApplication{ public static void main(String[] args) { CreateEncryptedDatabase theApp = new CreateEncryptedDatabase(); theApp.enterEventDispatcher(); }

public CreateEncryptedDatabase() { pushScreen(new CreateEncryptedDatabaseScreen()); }}

class CreateEncryptedDatabaseScreen extends MainScreen{ Database d; public CreateEncryptedDatabaseScreen() { LabelField title = new LabelField("SQLite Create Encrypted Database Sample", LabelField.ELLIPSIS | LabelField.USE_ALL_WIDTH); setTitle(title); add(new RichTextField("Creating an encrypted database called " + "MyEncryptedDatabase.db on the microSD card.")); try { URI myURI = URI.create("file:///SDCard/Databases/SQLite_Guide/" + "MyEncryptedDatabase.db"); DatabaseSecurityOptions dbso = new DatabaseSecurityOptions(true); d = DatabaseFactory.create(myURI,dbso);

Development Guide Security of SQLite databases

15

Page 18: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

d.close(); } catch ( Exception e ) { System.out.println( e.getMessage() ); e.printStackTrace(); } }}

Performance of SQLite databasesCompared to computers, smartphones provide a very constrained environment for SQLite® databases. To achieve optimal performance on a BlackBerry® device, consider the following constraints:

• The dynamic memory limit is 16 MB. This limit refers to the amount of RAM available to an SQLite database for storing internal data structures for schemas and transactions. The entire database schema is loaded into memory when an SQLite database is opened and persists until the database is closed.

• The maximum SQL query length is 1 MB.• The maximum number of databases that can be open at the same time is 56, and three to seven of those are

used by the Media application. The Database API uses a shared cache mode to make efficient use of memory for multiple connections to the same database.

• Only one read-write database connection to an SQLite database can be made at a time. Other database connections are read-only.

When you use the Database.createBlobOutputStream and Database.createBlobInputStream methods to read and write blobs, the memory available to SQLite does not restrict the size of the blob you can use.

You can track memory usage for each database connection using the following methods: Database.getCacheUsed, Database.getSchemaUsed, and Database.getStatementUsed.

Best practice: Optimizing SQLite database performanceConsider the following guidelines:

Best practice Description

Use the appropriate journal mode

By default, a rollback journal is used to record transactions, but you can choose to use a write-ahead log instead. The write-ahead log provides increased concurrency and is faster than the rollback journal in most situations because writing to the database does not block reading. However, the write-ahead log uses more file handles. The write-ahead log uses up to three file handles that can persist for long periods, while the rollback journal uses one, or occasionally two, during write transactions. You can use a write-ahead log by setting the WAL option in the journal_mode pragma.

Consider relaxing safety constraints for tables

When data integrity is not a concern, consider using pragma commands to specify no journal and turn synchronous mode off. For example, PRAGMA journal_mode=OFF and PRAGMA synchronous=OFF.

Development Guide Performance of SQLite databases

16

Page 19: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

Best practice Description

Test If you plan to create a database with a large schema or insert large blobs, you should test the database on your target BlackBerry® devices to make sure that the devices have adequate memory.

Store as little data as possible

Most of the processing time of SQLite® databases is taken up by reading and writing to storage. Less data generally means fewer reads and writes. The SQLite database engine caches frequently accessed database pages. By storing less data, you can increase the probability that the SQLite database engine retrieves requested data more quickly from the cache instead of from the relatively slow storage access.

Use explicit transactions

If you do not use explicit transactions, a transaction begins before each statement is executed and ends after the statement is executed. This default behavior is inefficient. It requires the opening, reopening, writing to, and closing of the journal file for each statement. With explicit transactions, you can group statements for greater efficiency.

Create efficient indexes

Indexes can greatly reduce the time required to scan a table. Consider the following guidelines:

• Indexes can improve performance for read-only queries such as SELECT, but they can reduce performance for queries that change rows. Consider only indexing tables that are infrequently updated.

• The order of columns in an index affects performance. Columns that are typically used in WHERE clauses should be placed first, followed by columns that are typically used in ORDER BY clauses.

• For columns containing data that is retrieved, create a covering index.• Avoid duplicate indexes. The SQLite database engine automatically creates indexes

for columns that have UNIQUE or PRIMARY KEY constraints.• Columns declared as INTEGER PRIMARY KEY result in faster queries because they have

direct access to table data.Minimize the size of rows

If you have a very wide column, consider putting it in a separate table.

Store blobs appropriately

If your data includes blobs, you should read and write them using the InputStream and OutputStream objects.

Consider storing each blob in a separate table. If the blobs are very large, you can store them as files outside the database (and store the path to each file in the database), but this practice introduces overhead for filename lookups.

Consider using temporary tables

If you do not need the data to be available following a restart of a BlackBerry device, use the CREATE TEMP TABLE statement instead of CREATE TABLE.

Use SQL parameters To execute a set of statements of the same format, first prepare a generic statement that uses SQL parameters. You can execute the statement by iterating through the variable values and binding the values to the named variables in each iteration.

Avoid subqueries In some cases, the SQLite database engine stores subquery results in a temporary file, which can slow down processing.

Development Guide Performance of SQLite databases

17

Page 20: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

Best practice Description

Defragment the database

Use the SQLite VACUUM command to defragment the database. This process also reduces the size of the database file.

Consider the order of columns in table declarations

The order of columns in a table declaration affects performance, especially in the absence of an index, because the SQLite database engine scans the columns in the order defined in the table declaration. Columns that contain small amounts of data that is frequently accessed should be placed before columns that contain large amounts of data that is infrequently accessed.

Use bulk operation methods

The following methods make efficient use of the runtime bridge:

• executeStatement• executeInsert and executeUpdate• getIntegers and getLongs• setCursorBufferSize

Creating and deleting SQLite databasesYou can create temporary or permanent databases.

External media card storage (a microSD card) is the preferred storage locations for databases if the BlackBerry® device supports it.

SQLite database filesEach 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 in external media card storage. 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 default path to avoid name collisions.

You cannot store SQLite databases in application storage.

External media card storage is the preferred storage location for databases if the BlackBerry® device supports it. On devices that support external media card storage, you can create databases in external media card storage by specifying the path /SDcard/.

If your application is designed to store your SQLite database in built-in media storage, you should implement your application so that it is easy to modify the code to change the storage location of the database. On devices that support built-in media storage, you can create databases in built-in media storage by specifying the path /store/ .

When your application is uninstalled, the SQLite databases associated with it are not automatically removed.

Character encodingSQLite® databases store strings internally in UTF-8 and Java® stores them as UTF-16. The Database API handles the conversion of strings internally, so you don't need to do any encoding or conversion.

Development Guide Creating and deleting SQLite databases

18

Page 21: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

The SQLite encoding pragma is not supported in the Database API, so you can't set another encoding. You must use UTF-8 supported characters in your SQLite database.

Create an SQLite databaseBefore you begin:

By default, database files are stored on a media card. If you are using a BlackBerry® Smartphone Simulator, you might need to Simulate a media card.

1. Import the required libraries.

import net.rim.device.api.ui.*;import net.rim.device.api.ui.component.*;import net.rim.device.api.ui.container.*;import net.rim.device.api.database.*;import net.rim.device.api.io.*;

2. Create the framework for the application by extending the UiApplication class. This class represents your application. Provide a main() method for the new class. In the main() method, create an instance of the new class and invoke the enterEventDispatcher() method to enable the application to receive events. Provide a constructor for the new class. In the constructor, invoke the pushScreen method to display the custom screen for the application.

public class CreateDatabase extends UiApplication{ public static void main(String[] args) { CreateDatabase theApp = new CreateDatabase(); theApp.enterEventDispatcher(); } public CreateDatabase() { pushScreen(new CreateDatabaseScreen()); } }

3. Create the screen for the application by extending the MainScreen class. Provide a constructor for the new class. In the constructor, create the title for the screen with a LabelField object and display it by invoking the setTitle() method. Invoke the add() method to display a text field on the screen.

class CreateDatabaseScreen extends MainScreen{ public CreateDatabaseScreen() { LabelField title = new LabelField("SQLite Create Database Sample", LabelField.ELLIPSIS | LabelField.USE_ALL_WIDTH); setTitle(title); add(new RichTextField("Creating a database called " +

Development Guide Creating and deleting SQLite databases

19

Page 22: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

"MyTestDatabase.db on the SDCard.")); }}

4. Create a URI that represents the database file by invoking the static create() method of the URI class. Invoke the create() method of the DatabaseFactory class to create the database that corresponds to the URI that you created. This creates an SQLite® database on the microSD card of the BlackBerry® device. If you do not specify a full path, the database is created in a folder named after your application.

try{ URI myURI = URI.create("file:///SDCard/Databases/SQLite_Guide/" + "MyTestDatabase.db"); Database db = DatabaseFactory.create(myURI);}catch ( Exception e ){ System.out.println( e.getMessage() ); e.printStackTrace();}

5. When you are finished using the database, it is good practice to close it.

db.close();

After you finish:

After creating a database, verify that the database file was created. You can do this in any of the following ways:

• View the database in a database viewer• Look in the file system for the database file, and verify that it is not zero size• Invoke DatabaseFactory.exists()

Code sample: Creating an SQLite databaseBy default, database files are stored on a media card. If you are using a BlackBerry® Smartphone Simulator, you might need to Simulate a media card.

import net.rim.device.api.ui.component.*;import net.rim.device.api.ui.container.*;import net.rim.device.api.database.*;import net.rim.device.api.io.*;import net.rim.device.api.ui.*;

public class CreateDatabase extends UiApplication{ public static void main(String[] args) { CreateDatabase theApp = new CreateDatabase(); theApp.enterEventDispatcher(); } public CreateDatabase() {

Development Guide Creating and deleting SQLite databases

20

Page 23: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

pushScreen(new CreateDatabaseScreen()); } }

class CreateDatabaseScreen extends MainScreen{ Database d; public CreateDatabaseScreen() { LabelField title = new LabelField("SQLite Create Database Sample", LabelField.ELLIPSIS | LabelField.USE_ALL_WIDTH); setTitle(title); add(new RichTextField("Creating a database called " + "MyTestDatabase.db on the SDCard.")); try { URI myURI = URI.create("file:///SDCard/Databases/SQLite_Guide/" + "MyTestDatabase.db"); d = DatabaseFactory.create(myURI); d.close(); } catch ( Exception e ) { System.out.println( e.getMessage() ); e.printStackTrace(); } }}

Code sample: Adding a schema to an SQLite database

import net.rim.device.api.ui.*;import net.rim.device.api.ui.component.*;import net.rim.device.api.ui.container.*;import net.rim.device.api.database.*;import net.rim.device.api.io.*;

public class CreateDatabaseSchema extends UiApplication{ public static void main(String[] args) { CreateDatabaseSchema theApp = new CreateDatabaseSchema(); theApp.enterEventDispatcher(); } public CreateDatabaseSchema() { pushScreen(new CreateDatabaseSchemaScreen()); } }

class CreateDatabaseSchemaScreen extends MainScreen{ Database d;

Development Guide Creating and deleting SQLite databases

21

Page 24: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

public CreateDatabaseSchemaScreen() { LabelField title = new LabelField("SQLite Create " + "Database Schema Sample", LabelField.ELLIPSIS | LabelField.USE_ALL_WIDTH); setTitle(title); add(new RichTextField("Adding a table to a database called " + "MyTestDatabase.db on the SDCard.")); try { URI myURI = URI.create("file:///SDCard/Databases/SQLite_Guide/" + "MyTestDatabase.db"); d = DatabaseFactory.openOrCreate(myURI); Statement st = d.createStatement( "CREATE TABLE People ( " + "Name TEXT, " + "Age INTEGER )" ); st.prepare(); st.execute(); st.close(); d.close(); } catch ( Exception e ) { System.out.println( e.getMessage() ); e.printStackTrace(); } }}

Code sample: Deleting an SQLite database

import net.rim.device.api.ui.*;import net.rim.device.api.ui.component.*;import net.rim.device.api.ui.container.*;import net.rim.device.api.database.*;import net.rim.device.api.io.*;

public class DeleteDatabase extends UiApplication{ public static void main(String[] args) { DeleteDatabase theApp = new DeleteDatabase(); theApp.enterEventDispatcher(); } public DeleteDatabase() { pushScreen(new DeleteDatabaseScreen()); } }

class DeleteDatabaseScreen extends MainScreen

Development Guide Creating and deleting SQLite databases

22

Page 25: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

{ Database d; public DeleteDatabaseScreen() { LabelField title = new LabelField("SQLite Delete Database Sample", LabelField.ELLIPSIS | LabelField.USE_ALL_WIDTH); setTitle(title); add(new RichTextField("Deleting a database called " + "MyTestDatabase.db on the SDCard.")); try { URI myURI = URI.create("file:///SDCard/Databases/SQLite_Guide/" + "MyTestDatabase.db"); DatabaseFactory.delete(myURI); } catch ( Exception e ) { System.out.println( e.getMessage() ); e.printStackTrace(); } }}

Working with SQLite databasesOnce you have created an SQLite® database, you can use SQL statements to add data, retrieve data, and modify the database.

The list of supported SQL statements and their syntax is available on the SQLite web site. The Database API does not support the following SQLite statements: ATTACH DATABASE, DETACH DATABASE, and PRAGMA. In addition, FTS2 and RTREE are not supported.

The following steps outline the basic procedure for running statements:

1. Create an SQL statement by invoking Database.createStatement() .

2. Prepare the statement to run by invoking Statement.prepare() .

3. Run the statement. If the statement might return results, run it by invoking Statement.getCursor() . Otherwise, use Statement.execute() .

4. If the statement returns a result set, retrieve the result set by iterating over the returned cursor row by row. Do this using the Cursor interface, which works in all circumstances but is forward-only. For birectional cursor movement, but only for small result sets, use the BufferedCursor class.

Using transactionsSQLite® statements always run in transactions. If the statement runs successfully, the transaction is automatically committed. If the statement fails, the transaction is rolled back.

Development Guide Working with SQLite databases

23

Page 26: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

By default, a separate transaction is created for each SQLite statement, which is less efficient than running multiple statements in one transaction. You can usually improve performance by explicitly specifying transactions for groups of statements. You can run multiple statements in one transaction using Database.beginTransaction() and Database.commitTransaction() around groups of statements.

Nested transactions are not supported.

Code sample: Using transactions

import net.rim.device.api.ui.*;import net.rim.device.api.ui.component.*;import net.rim.device.api.ui.container.*;import net.rim.device.api.database.*;import net.rim.device.api.io.*;public class UsingTransactions extends UiApplication{ public static void main(String[] args) { UsingTransactions theApp = new UsingTransactions(); theApp.enterEventDispatcher(); } public UsingTransactions() { }}

class UsingTransactionsScreen extends MainScreen{ Database d; public UsingTransactionsScreen() { LabelField title = new LabelField("SQLite Using Transactions Sample", LabelField.ELLIPSIS | LabelField.USE_ALL_WIDTH); setTitle(title); add(new RichTextField( "Updating data in one transaction in MyTestDatabase.db.")); try { URI myURI = URI.create("file:///SDCard/Databases/SQLite_Guide/" + "MyTestDatabase.db"); d = DatabaseFactory.open(myURI); d.beginTransaction(); Statement st = d.createStatement("UPDATE People SET Age=7 " + "WHERE Name='Sophie'"); st.prepare(); st.execute(); st.close(); st = d.createStatement("UPDATE People SET Age=4 " + "WHERE Name='Karen'"); st.prepare(); st.execute(); st.close();

Development Guide Working with SQLite databases

24

Page 27: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

d.commitTransaction(); d.close(); } catch ( Exception e ) { System.out.println( e.getMessage() ); e.printStackTrace(); } }}

Using SQL parametersWhen you create an SQL statement, you can create SQL parameters in order to reuse the statement with different values. This practice can provide performance benefits. Prepare generic statements that use named variables, and then execute the statements when they are required by iterating through the variable values, binding the values to the named variables in each iteration.

You can choose from the following ways to number the parameters:

• A question mark (?) in the statement causes each parameter to be numbered sequentially, starting from 1.• A question mark followed by an integer (?NNN) in the statement provides each parameter with the number NNN.

You can use the Statement.bind() method to provide names for SQL parameters. The bind() method takes the number of the parameter and the value to be bound to it. If you use a number outside of the allowed range, a DatabaseException is thrown. All bindings can be reset using Statement.reset() .

Here's an example of a statement that uses parameters to create an upper bound and lower bound that can be defined each time the statement is run. This example numbers the parameters sequentially.

Statement s = Database.createStatement("SELECT * FROM T WHERE a < ? AND a > ?"); s.prepare(); s.bind(1, upperBound); s.bind(2, lowerBound); Cursor c = s.getCursor();

Here's an example of the same statement, except that explicit numbers are specified for the parameters:

Statement s = Database.createStatement("SELECT * FROM T WHERE a < ?5 AND a > ?12"); s.prepare(); s.bind(5, upperBound); s.bind(12, lowerBound);

The getFormalName() method converts a parameter index to an SQL parameter name. For getFormalName() to be able to return the parameter name, you must provide a name in the query. For example, when you call getFormalName(1), the statement "SELECT * FROM T WHERE a = :a" returns :a. When parameters such as a question mark (?) are used as placeholders, getFormalName() cannot return a parameter name. For example, getFormalName(1) will not return the name for the parameter in this statement: "SELECT * FROM T WHERE a = ?"

Development Guide Working with SQLite databases

25

Page 28: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

Code sample: Creating a parameterized update

import net.rim.device.api.ui.*;import net.rim.device.api.ui.component.*;import net.rim.device.api.ui.container.*;import net.rim.device.api.database.*;import net.rim.device.api.io.*;import java.util.*;

public class ParameterizedUpdate extends UiApplication{ public static void main(String[] args) { ParameterizedUpdate theApp = new ParameterizedUpdate(); theApp.enterEventDispatcher(); } public ParameterizedUpdate() { pushScreen(new ParameterizedUpdateScreen()); } } class ParameterizedUpdateScreen extends MainScreen { Database d; public ParameterizedUpdateScreen() { LabelField title = new LabelField("SQLite Parameterized Update Sample", LabelField.ELLIPSIS | LabelField.USE_ALL_WIDTH); setTitle(title); add(new RichTextField("Attempting to update data in " + "MyTestDatabase.db on the SDCard.")); try { URI myURI = URI.create("file:///SDCard/Databases/SQLite_Guide/" + "MyTestDatabase.db"); d = DatabaseFactory.open(myURI); Statement st = d.createStatement( "UPDATE People SET Age=? WHERE Name=?"); st.prepare(); Hashtable ht = new Hashtable(2); ht.put("Sophie", new Integer(10)); ht.put("Karen", new Integer(7)); Enumeration names = ht.keys(); Enumeration ages = ht.elements(); while (names.hasMoreElements()) { Integer iAge = (Integer)ages.nextElement(); String strName = (String)names.nextElement(); st.bind(1,iAge.intValue()); st.bind(2,strName);

Development Guide Working with SQLite databases

26

Page 29: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

st.execute(); st.reset(); } st.close(); d.close(); } catch ( Exception e ) { System.out.println( e.getMessage() ); e.printStackTrace(); } }}

Code sample: Creating a parameterized insert

import net.rim.device.api.ui.*;import net.rim.device.api.ui.component.*;import net.rim.device.api.ui.container.*;import net.rim.device.api.database.*;import net.rim.device.api.io.*;import java.util.*;

public class ParameterizedInsert extends UiApplication{public static void main(String[] args){ ParameterizedInsert theApp = new ParameterizedInsert(); theApp.enterEventDispatcher();}public ParameterizedInsert(){ pushScreen(new ParameterizedInsertScreen());}

}class ParameterizedInsertScreen extends MainScreen{ Database d; public ParameterizedInsertScreen() { LabelField title = new LabelField("SQLite Insert Data " + "Schema Sample", LabelField.ELLIPSIS | LabelField.USE_ALL_WIDTH); setTitle(title); add(new RichTextField("Attempting to insert data into " + "MyTestDatabase.db on the SDCard.")); try { URI myURI = URI.create("file:///SDCard/Databases/SQLite_Guide/" + "MyTestDatabase.db"); d = DatabaseFactory.open(myURI);

Statement st = d.createStatement("INSERT INTO People(Name,Age) " +

Development Guide Working with SQLite databases

27

Page 30: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

"VALUES (?,?)"); st.prepare();

Hashtable ht = new Hashtable(4); ht.put("Sophie", new Integer(6)); ht.put("Karen", new Integer(3)); ht.put("Kevin", new Integer(82)); ht.put("Cindy", new Integer(12));

Enumeration names = ht.keys(); Enumeration ages = ht.elements();

while (names.hasMoreElements()) { String strName = (String)names.nextElement(); Integer iAge = (Integer)ages.nextElement(); st.bind(1,strName); st.bind(2,iAge.intValue()); st.execute(); st.reset(); } st.close(); d.close();

} catch ( Exception e ) { System.out.println( e.getMessage() ); e.printStackTrace(); }}}

Using foreign key constraintsIf you create foreign keys for your database, you can enforce the use of them by setting the foreign_key_constraints option in the DatabaseOptions class.

You can set this option when you create the database. For the foreign key constraints to be in effect, you must set the option each time you open the database.

You can check whether foreign key constraints are on with the DatabaseOptions.foreignKeyConstraintsOn method.

Code sample

The following code sample shows how to enforce foreign key constraints before opening or creating a database. The code sample creates the DatabaseOptions object with foreign_key_constraints set to on.

DatabaseOptions dbo = new DatabaseOptions();dbo.set("foreign_key_constraints","on");Database d = DatabaseFactory.openOrCreate("test.db", dbo);

Development Guide Working with SQLite databases

28

Page 31: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

Code sample: Inserting table data

import net.rim.device.api.ui.*;import net.rim.device.api.ui.component.*;import net.rim.device.api.ui.container.*;import net.rim.device.api.database.*;import net.rim.device.api.io.*;

public class InsertData extends UiApplication{ public static void main(String[] args) { InsertData theApp = new InsertData(); theApp.enterEventDispatcher(); } public InsertData() { pushScreen(new InsertDataScreen()); } }

class InsertDataScreen extends MainScreen{ Database d; public InsertDataScreen() { LabelField title = new LabelField("SQLite Insert Data " + "Schema Sample", LabelField.ELLIPSIS | LabelField.USE_ALL_WIDTH); setTitle(title); add(new RichTextField("Attempting to insert data into " + "MyTestDatabase.db on the SDCard.")); try { URI myURI = URI.create("file:///SDCard/Databases/SQLite_Guide/" + "MyTestDatabase.db"); d = DatabaseFactory.open(myURI); Statement st = d.createStatement("INSERT INTO People(Name,Age) " + "VALUES ('John',37)"); st.prepare(); st.execute(); st.close(); d.close();

} catch ( Exception e ) { System.out.println( e.getMessage() ); e.printStackTrace(); }

Development Guide Working with SQLite databases

29

Page 32: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

}}

Code sample: Retrieving table data

import net.rim.device.api.ui.*;import net.rim.device.api.ui.component.*;import net.rim.device.api.ui.container.*;import net.rim.device.api.database.*;import net.rim.device.api.io.*;

public class ReadData extends UiApplication{ public static void main(String[] args) { ReadData theApp = new ReadData(); theApp.enterEventDispatcher(); } public ReadData() { pushScreen(new ReadDataScreen()); } }

class ReadDataScreen extends MainScreen{ Database d; public ReadDataScreen() { LabelField title = new LabelField("SQLite Read Table Data Sample", LabelField.ELLIPSIS | LabelField.USE_ALL_WIDTH); setTitle(title); add(new RichTextField("Attempting to retrieve data from " + "MyTestDatabase.db on the SDCard.")); try { URI myURI = URI.create("file:///SDCard/Databases/SQLite_Guide/" + "MyTestDatabase.db"); d = DatabaseFactory.open(myURI); Statement st = d.createStatement("SELECT Name,Age FROM People");

st.prepare(); Cursor c = st.getCursor(); Row r; int i = 0; while(c.next()) { r = c.getRow(); i++; add(new RichTextField(i + ". Name = " + r.getString(0) +

Development Guide Working with SQLite databases

30

Page 33: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

" , " + "Age = " + r.getInteger(1))); } if (i==0) { add(new RichTextField("No data in the People table.")); } st.close(); d.close();

} catch ( Exception e ) { System.out.println( e.getMessage() ); e.printStackTrace(); } }}

Code sample: Deleting table dataThe following code sample illustrates the DELETE statement.

import net.rim.device.api.ui.*;import net.rim.device.api.ui.component.*;import net.rim.device.api.ui.container.*;import net.rim.device.api.database.*;import net.rim.device.api.io.*;

public class DeleteData extends UiApplication{ public static void main(String[] args) { DeleteData theApp = new DeleteData(); theApp.enterEventDispatcher(); } public DeleteData() { pushScreen(new DeleteDataScreen()); } }

class DeleteDataScreen extends MainScreen{ Database d; public DeleteDataScreen() { LabelField title = new LabelField("SQLite Delete Database Data", LabelField.ELLIPSIS | LabelField.USE_ALL_WIDTH); setTitle(title); add(new RichTextField("Attempting to delete data from " + "MyTestDatabase.db on the SDCard.")); try

Development Guide Working with SQLite databases

31

Page 34: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

{ URI myURI = URI.create("file:///SDCard/Databases/SQLite_Guide/" + "MyTestDatabase.db"); d = DatabaseFactory.open(myURI); Statement st = d.createStatement("DELETE FROM People"); st.prepare(); st.execute(); st.close(); d.close();

} catch ( Exception e ) { System.out.println( e.getMessage() ); e.printStackTrace(); } }}

Code sample: Updating table data

import net.rim.device.api.ui.*;import net.rim.device.api.ui.component.*;import net.rim.device.api.ui.container.*;import net.rim.device.api.database.*;import net.rim.device.api.io.*;

public class UpdateData extends UiApplication{ public static void main(String[] args) { UpdateData theApp = new UpdateData(); theApp.enterEventDispatcher(); } public UpdateData() { pushScreen(new UpdateDataScreen()); } }

class UpdateDataScreen extends MainScreen{ Database d; public UpdateDataScreen() { LabelField title = new LabelField("SQLite Update Data Sample", LabelField.ELLIPSIS | LabelField.USE_ALL_WIDTH); setTitle(title); add(new RichTextField("Trying to update data in MyTestDatabase.db.")); try {

Development Guide Working with SQLite databases

32

Page 35: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

URI myURI = URI.create("file:///SDCard/Databases/SQLite_Guide/" + "MyTestDatabase.db"); d = DatabaseFactory.open(myURI); Statement st = d.createStatement("UPDATE People SET Age=38 " + "WHERE Name='John'"); st.prepare(); st.execute(); st.close(); d.close();

} catch ( Exception e ) { System.out.println( e.getMessage() ); e.printStackTrace(); } }}

Code sample: Listing database tables

import net.rim.device.api.ui.*;import net.rim.device.api.ui.component.*;import net.rim.device.api.ui.container.*;import net.rim.device.api.database.*;import net.rim.device.api.io.*;

public class ListTables extends UiApplication{ public static void main(String[] args) { ListTables theApp = new ListTables(); theApp.enterEventDispatcher(); } public ListTables() { pushScreen(new ListTablesScreen()); } }

class ListTablesScreen extends MainScreen{ Database d; public ListTablesScreen() { LabelField title = new LabelField("SQLite List Database Tables", LabelField.ELLIPSIS | LabelField.USE_ALL_WIDTH); setTitle(title); add(new RichTextField("Attempting to list tables in " + "MyTestDatabase.db on the SDCard.")); try

Development Guide Working with SQLite databases

33

Page 36: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

{ URI myURI = URI.create("file:///SDCard/Databases/SQLite_Guide/" + "MyTestDatabase.db"); d = DatabaseFactory.open(myURI); Statement st = d.createStatement("SELECT name FROM " + " sqlite_master " + "WHERE type='table'" + "ORDER BY name"); st.prepare(); Cursor c = st.getCursor(); Row r; int i = 0; while(c.next()) { r = c.getRow(); i++; add(new RichTextField(i + ". Table: " + r.getString(0))); } if (i==0) { add(new RichTextField("There are no tables " + " in the MyTestDatabase database.")); } st.close(); d.close(); } catch ( Exception e ) { System.out.println( e.getMessage() ); e.printStackTrace(); } }}

SQLite sample application

OverviewThe SQLite® database sample application demonstrates how to create a persistent relational database that is stored on the BlackBerry® device and how to change the entries in the database.

The database contains two tables that are called Category and DirectoryItems. The DirectoryItems table contains items that simulate entries in a business directory list. Each DirectoryItem entry also contains a CategoryID field that must match a category_id entry in the Category table (for instance, Category.category_id is a foreign key for DirectoryItem.categoryID).

Development Guide SQLite sample application

34

Page 37: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

The sample application displays the entries in the tables as a collapsible tree structure with Category entries as parent nodes and DirectoryItem entries as child nodes of the Category nodes that they are associated with. The sample application provides menu items for adding a new category or directory item and for changing or deleting the directory item.

This sample application uses APIs that are designed to be secure.The application must be signed before it can be run. For more information about code signing, see the BlackBerry Signing Authority Tool Administration Guide.

Files in the sample application

File name Description

SQLiteDemo.java • This file contains the application entry point.• This file contains the application's constructor, which creates an

encrypted database on the media card of the BlackBerry® device if it is inserted or in device memory. If a media card is not inserted and the BlackBerry device supports storing the database in device memory, the encrypted database is created in device memory.

• This file creates and displays an instance of SQLiteDemoScreen.SQLiteDemoScreen.java • This file contains the application's main screen, which displays the

database in a collapsible tree structure with Category entries as parent nodes and associated DirectoryItems as their children.

• This file contains helper methods to populate the tree when the screen is initialized.

• This file contains menu items for adding, updating, and deleting categories and directory items.

SQLManager.java This file contains all the methods for adding, updating, and deleting categories and directory items (for instance, all of the database manipulation methods).

ItemScreen.java This file contains the screen, which contains fields for adding and changing categories and directory items.

Category.java • This file defines the Category entries in the database.• This file provides accessor methods for the Category fields.

DirectoryItems.java • This file defines the DirectoryItems entries in the database.• This file provides accessor methods for the DirectoryItems fields.

SQLiteDemoDirectory • This file contains the database.• This file comes prepopulated with sample entries.

Featured interfacesnet.rim.device.api.Database

Development Guide SQLite sample application

35

Page 38: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

The Database interface represents the database. This interface provides methods for changing a database on the BlackBerry device, including methods for creating SQL statements to add, delete, or update records in the database and methods for committing and canceling transactions in the database. This interface also provides methods for setting and retrieving the database's metadata.

To create the database, you can use one of the methods of the DatabaseFactory class.

The sample application uses this interface to create statements to insert, delete, and update records in the database.

net.rim.device.api.Statement

The Statement interface represents an SQL statement. You can use the Statement interface to retrieve, add, delete, or change entries in the database.

net.rim.device.api.Cursor

The Cursor interface provides methods for traversing a result set that is retrieved using the Statement object. A Statement object creates the Cursor object using the Statement.getCursor() method as part of its query process. Therefore, a Cursor is always associated with a specific query.

Featured classesnet.rim.device.api.database.Row

The Row class represents a row in a result set that a SELECT query returns.

This class provides methods for retrieving column indices in the Row object and the values stored in the Row object's columns.

You create this class using the Cursor.getRow() method.

net.rim.device.api.database.DatabaseFactory

The DatabaseFactory class provides methods for creating, opening, configuring, and deleting a new or existing Database. Configuration options include persistent or nonpersistent storage and encryption.

To create a new database, you must use the DatabaseFactory.openOrCreate() method or the DatabaseFactory.create() method. If you have already created the database, the DatabaseFactory.openOrCreate() method returns a reference to the database.

Create the database on a media card instead of in device memory. Only certain BlackBerry® devices support storing an SQLite database in device memory.

Install the sample application

1. In Eclipse®, on the File menu, click Import.

2. In the Import dialog box, expand the BlackBerry folder.

3. Click Import BlackBerry Samples.

4. Click Next.

Development Guide SQLite sample application

36

Page 39: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

5. Perform one of the following actions:• To specify a specific JRE™, select the Use a project specific JRE option.• To specify the default JRE in the workspace, select the Use default JRE option.

6. In the BlackBerry Projects section, click Deselect All.

7. Select the check box beside the SQLiteDemo project.

8. Click Finish.

Run the sample application

1. In Eclipse®, in the Navigator pane, right-click the SQLiteDemo folder.

2. Click Run As > BlackBerry Simulator.

3. If the BlackBerry® Smartphone Simulator does not display the message "Media card inserted", you must Simulate a media card.

4. If necessary, on the Home screen of the BlackBerry® Smartphone Simulator, click the Downloads folder.

5. Click the SQLite Demo icon.

Development Guide SQLite sample application

37

Page 40: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

Storing objects persistently 4The persistent store lets you save objects to persistent memory. The objects are retained in memory after a BlackBerry® device restarts. The persistent store is included in all versions of BlackBerry® Device Software.

With the Persistent Store API, you can save entire Java® objects to memory without having to serialize the data first. When your application starts, it can retrieve the Java object from memory and process the data. The Persistent Store API does not provide a relational database model. You must create an effective object model and manage the relationships between objects, as necessary, using indices and hash tables.

The Persistent Store API is implemented in the PersistentObject class, PersistentStore class, and EventLogger class, all of which are provided in the net.rim.device.api.system package, and the Persistable interface, which is provided in the net.rim.device.api.util package.

Data is stored as instances of PersistentObject. PersistentObject can be any object that implements the Persistable interface. The Persistent Store API allows the implicit persistence of classes, so the following data types automatically implement the Persistable interface and can also be stored in the persistent store:

• java.lang.Boolean• java.lang.Byte• java.lang.Character• java.lang.Integer• java.lang.Long• java.lang.Object• java.lang.Short• java.lang.String• java.util.Vector• java.util.Hashtable

The storage for each application is distinct because each object in the persistent store is associated with a 64-bit ID (type long).

Security of persistent objectsBlackBerry® Device Software provides two main ways to secure objects in the persistent store:

• Restrict access to the objects with the net.rim.device.api.system.ControlledAccess class and code signing keys.

• Encrypt and decrypt objects with the net.rim.device.api.system.PersistentContent class.

Depending on the needs of your application, you could use both, one, or neither of these approaches.

Development Guide Storing objects persistently

38

Page 41: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

Restricting access to persistent objectsObjects that are stored using the persistent store are identified by a unique key, which has a value that is of primitive type long. To retrieve an object from the persistent store, the key that was associated with the object when it was stored must be supplied in method calls. For example:

long key = 0x52834c0559055c3L;PersistentObject rec = PersistentStore.getPersistentObject(key);String stored_value = (String) rec.getContents();

Relying on the key value being unknown may not provide adequate privacy for stored information. If you want to permit only specific, authorized applications to access your application data, you should wrap the PersistentObject in a net.rim.device.api.system.ControlledAccess class in conjunction with key generation and a key-signing procedure:

• Create a signing key using the BlackBerry® Signing Authority Tool.• Associate a ControlledAccess object with the signing key.• Wrap the PersistentObject in the ControlledAccess object.• Sign applications that require access to the protected data using the same signing key that protects the data.

Here is an example of how to retrieve data that is protected with the ControlledAccess class:

long key = 0x52834c0559055c3L;try { PersistentObject rec = PersistentStore.getPersistentObject(key); int moduleHandle = ApplicationDescriptor.currentApplicationDescriptor().getModuleHandle(); CodeSigningKey codeSigningKey = CodeSigningKey.get( moduleHandle, "MDW" ); String b = (String) rec.getContents( codeSigningKey ); Dialog.inform("Read PersistentObject. Value="+b);} catch (ControlledAccessException e) { Dialog.alert("ControlledAccessException - not authorised to read this data");}

For more information about restricting access to persistent objects, see Protect persistent objects from access by unauthorized applications.

For more information about code signing, see the BlackBerry Java SDK Security Development Guide, available at www.blackberry.com/go/devguides.

Code sample

The .cod file for the following code must be signed using the key that is used as the CodeSigningKey to protect the data.

import net.rim.device.api.system.ApplicationDescriptor;import net.rim.device.api.system.CodeSigningKey;import net.rim.device.api.system.ControlledAccess;import net.rim.device.api.system.PersistentObject;import net.rim.device.api.system.PersistentStore;import net.rim.device.api.ui.*;

Development Guide Security of persistent objects

39

Page 42: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

public class ControlledAccessCreator extends UiApplication { public static void main(String[] args) { ControlledAccessCreator cac = new ControlledAccessCreator(); cac.enterEventDispatcher(); } private ControlledAccessCreator() { ControlledAccessCreatorScreen screen = new ControlledAccessCreatorScreen(); pushScreen(screen); }}

class ControlledAccessCreatorScreen extends MainScreen { private MenuItem _initialiseMenuItem = new MenuItem("Initialise", 100, 10) { public void run() { String a = "Hello"; long key = 0x52834c0559055c3L; PersistentObject rec = PersistentStore.getPersistentObject(key); int moduleHandle = ApplicationDescriptor.currentApplicationDescriptor().getModuleHandle(); CodeSigningKey codeSigningKey = CodeSigningKey.get( moduleHandle, "MDW" ); rec.setContents(new ControlledAccess(a,codeSigningKey)); rec.commit(); String b = (String) rec.getContents( codeSigningKey ); Dialog.inform("Initialised... now try to acccess the data from another app"); invalidate(); } }; ControlledAccessCreatorScreen() { setTitle(new LabelField("ControlledAccess Demo", LabelField.USE_ALL_WIDTH)); addMenuItem(_initialiseMenuItem); } public boolean onSavePrompt() { return true; } public void close() { super.close(); }}

The .cod file for the following code must be signed using the key that was used for the sample code above.

import net.rim.device.api.system.ApplicationDescriptor;import net.rim.device.api.system.CodeSigningKey;import net.rim.device.api.system.ControlledAccess;import net.rim.device.api.system.ControlledAccessException;import net.rim.device.api.system.PersistentObject;import net.rim.device.api.system.PersistentStore;import net.rim.device.api.ui.*;

public class ControlledAccessAuthorisedUser extends UiApplication { public static void main(String[] args) { ControlledAccessAuthorisedUser caau = new ControlledAccessAuthorisedUser(); caau.enterEventDispatcher(); }

Development Guide Security of persistent objects

40

Page 43: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

private ControlledAccessAuthorisedUser() { ControlledAccessAuthorisedUserScreen screen = new ControlledAccessAuthorisedUserScreen(); pushScreen(screen); }}class ControlledAccessAuthorisedUserScreen extends MainScreen { private MenuItem _readMenuItem = new MenuItem("Read protected data", 100, 10) { public void run() { long key = 0x52834c0559055c3L; try { PersistentObject rec = PersistentStore.getPersistentObject(key); int moduleHandle = ApplicationDescriptor.currentApplicationDescriptor().getModuleHandle(); CodeSigningKey codeSigningKey = CodeSigningKey.get( moduleHandle, "MDW" ); String b = (String) rec.getContents( codeSigningKey ); Dialog.inform("Read PersistentObject. Value="+b); } catch (ControlledAccessException e) { Dialog.alert("ControlledAccessException - not authorised to read this data"); } } }; ControlledAccessAuthorisedUserScreen() { setTitle(new LabelField("ControlledAccess Demo", LabelField.USE_ALL_WIDTH)); addMenuItem(_readMenuItem); }

public boolean onSavePrompt() { return true; } public void close() { super.close(); }}

Restrict access to persistent store data using code signing keys

Code signing keys can be used to control access to the persistent store. This is a way to restrict or share access with other apps on a BlackBerry® device.

1. Import the required classes and interfaces.

import java.util.Hashtable;import net.rim.device.api.system.PersistentObject;

2. Create a hash ID for the object you want to store in a persistent object.

long MY_DATA_ID = 0x33abf322367f9018L;Hashtable myHashtable = new Hashtable();

3. Store the object in the persistent object and protect the object with the CodeSigningKey object. For example, after a BlackBerry device application runs the following line of code, only .cod files that are signed with the RSAE .key file can read or overwrite the object in the persistent object.

Development Guide Security of persistent objects

41

Page 44: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

persistentObject.setContents( new ControlledAccess( myHashtable, key ) );

4. Make sure that the object is protected, and invoke getContents using the CodeSigningKey object as a parameter.

Hashtable myHashtable = (Hashtable) persistentObject.getContents( key );

Performance of the persistent storeThe persistent store exists in application storage. There is more latency in writing to application storage than there is in reading from it. Reading from the persistent store is relatively fast while commits are relatively slow.

Best practice: Using efficient data structure selectionData structure selection defines how many object handles and how much flash memory a BlackBerry® Java Application consumes. Improper data structure selection can consume key resources without improving the BlackBerry Java Application functionality or the BlackBerry device user experience.

Consider the following guidelines:

• The data structure should consist of the least possible number of objects, especially when you use high-level objects like a Vector or a Hashtable. These classes provide significant functionality but are not efficient storage mechanisms and you should avoid using them in the persistent store if possible.

• When possible, use primitives instead of objects, because primitives reduce the number of object handles that are consumed on the BlackBerry device. An array of primitives is an object and consumes an object handle.

• String objects are as efficient as byte arrays. A String object consumes only one object handle and is equivalent if your application stores all of the characters as a byte. In other words, the value of each character is less than or equal to the decimal value of 255. If your application cannot store characters as a byte, you can store the characters as a String, which is equivalent to storing a char array.

Best practice: Conserving object handlesOne of the most common errors that application developers encounter is an exhaustion of persistent object handles. The amount of application storage space on the BlackBerry® device determines the fixed number of persistent object handles that are available in the system. Depending on the data structure selection, stored records can quickly exhaust the number of persistent object handles. A persistent object consumes a persistent object handle and an object handle. A transient object consumes only an object handle.

For example, a record that contains ten String fields, which represent items such as a name, a phone number, and an address, consumes 11 persistent object handles, one for the record object and one for each String. If an application persists 3000 records, the application consumes 33,000 persistent object handles, which exceeds the number of persistent object handles available on a BlackBerry device with 16 MB of flash memory.

You can use the net.rim.device.api.system.ObjectGroup class to consolidate the object handles for an object into one group. Using the example in the previous paragraph, if you group the record, the record consumes one persistent object handle instead of 11. The object handles for the String fields consolidate under the record object handle.

Development Guide Performance of the persistent store

42

Page 45: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

When you consolidate object handles into one group, the object handle is read-only. You must ungroup the object before you can change it. After you complete the changes, group the object again. If you attempt to change a grouped object without first ungrouping it, an ObjectGroupReadOnlyException is thrown.

Ungrouping an object has a performance impact. The system creates a copy of the grouped object and allocates handles to each of the objects inside that group. Therefore, objects should only be ungrouped when necessary.

It is possible for commits to the persistent store to occur during garbage collection without an explicit commit(), so grouping of objects should always occur before calls to setContents() or commit(). For more information about object grouping, see net.rim.device.api.system.ObjectGroup .

Cleanup of persistent objectsWhen an application is removed from a BlackBerry® device, 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 removed, 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.

To delete individual data, treat the data as normal objects, and remove references to it. A garbage collection operation removes the data.

Creating a persistent storeTo create a persistent store, you create at least one PersistentObject. Each PersistentObject has a unique key of type long.

Create a persistent data storeEach PersistentObject has a unique long key.

1. Import the required classes and interfaces.

import net.rim.device.api.system.PersistentObject;import net.rim.device.api.system.PersistentStore;import java.lang.String;import net.rim.device.api.ui.component.Dialog;

2. To create a unique long key, in the BlackBerry® Integrated Development Environment, type a string value. For exmaple, com.rim.samples.docs.userinfo

3. Right-click the string and click Convert ‘com.rim.samples.docs.userinfo’ to long.

4. Include a comment in your code to indicate the string that you used to generate the unique long key.

5. To create a persistent data store, create a single static PersistentObject and invoke PersistentStore.getPersistentObject, using the unique long key as a parameter.

Development Guide Creating a persistent store

43

Page 46: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

static PersistentObject store;static {store = PersistentStore.getPersistentObject( 0xa1a569278238dad2L );}

Store persistent data

1. Import the required classes and interfaces.

import net.rim.device.api.system.PersistentObject;import net.rim.device.api.system.PersistentStore;

2. Invoke setContents() on a PersistentObject. This method replaces the existing content with the new content.

3. To save the new content to the persistent store, invoke commit().

String[] userinfo = {username, password};synchronized(store) {store.setContents(userinfo); store.commit();}

4. To use a batch transaction to commit objects to the persistent store, invoke PersistentStore.getSynchObject(). This method retrieves the persistent store monitor that locks the object.

a. Synchronize on the object.

b. Invoke commit() as necessary. If any commit in the batch fails, the entire batch transaction fails.

5. To commit a monitor object separately from a batch transaction, invoke forceCommit() while synchronizing the monitor object.

Store an object in a batch transaction

1. To use a batch transaction to commit objects to the persistent store, invoke PersistentStore.getSynchObject(). This method retrieves the persistent store monitor that locks the object.

2. Synchronize on the object.

3. Invoke commit() as necessary. If any commit in the batch fails, the entire batch transaction fails.

Working with the persistent storeYou can retrieve and remove objects and collections from the persistent store.

Retrieve persistent data

1. Import the required classes and interfaces.

Development Guide Working with the persistent store

44

Page 47: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

import net.rim.device.api.system.PersistentObject;import net.rim.device.api.ui.component.Dialog;

2. Invoke getContents() on a PersistentObject.

3. To convert to your desired format, perform an explicit cast on the object that PersistentObject.getContents() returns.

synchronized(store) {String[] currentinfo = (String[])store.getContents(); if(currentinfo == null) {Dialog.alert(_resources.getString(APP_ERROR));} else {currentusernamefield.setText(currentinfo[0]);currentpasswordfield.setText(currentinfo[1]);}}

Retrieve a collection from persistent storage

1. Import the required classes and interfaces.

import java.util.Vector;import net.rim.device.api.system.PersistentStore;import net.rim.device.api.synchronization.SyncCollection;

2. To provide the BlackBerry® device application with access to the newest SyncCollection data from the PersistentStore, invoke the PersistentStore.getPersistentObject() method using the ID of the SyncCollection.

private PersistentObject _persist; private Vector _contacts; private static final long PERSISTENT_KEY = 0x266babf899b20b56L;_persist = PersistentStore.getPersistentObject( PERSISTENT_KEY );

3. Store the returned data in a vector object._contacts = (Vector)_persist.getContents();

4. Create a method to provide the BlackBerry device application with the newest SyncCollection data before a wireless data backup session begins.

public void beginTransaction(){_persist = PersistentStore.getPersistentObject(PERSISTENT_KEY); _contacts = (Vector)_persist.getContents();}

5. Create code to manage the case where the SyncCollection you retrieve from the PersistentStore is empty.

if( _contacts == null ){_contacts = new Vector();

Development Guide Working with the persistent store

45

Page 48: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

_persist.setContents( _contacts );_persist.commit();}

Development Guide Working with the persistent store

46

Page 49: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

Storing objects nonpersistently 5The runtime store provides a central location for applications to store and share information on a 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.

The runtime store is implemented in the net.rim.device.api.system.RuntimeStore class.

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. You can generate the unique ID in the Eclipse® editor by right-clicking the fully-qualified class name and clicking Convert 'name' to long.

Note: Before your application closes, remove objects from the runtime store that your application no longer requires. If you add an object instance to the runtime store and don't remove it, you could create a memory leak.

Common uses of the runtime storeYou 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.

Here are some common uses of the runtime store:

Share data between two applications

For example, an application suite could be made up of multiple applications, all of which use data that is pushed to the device. One of the applications receives all the push data and shares it with the other applications by temporarily storing the data in the runtime store. The runtime store could also be used to set up communication between a listener (such as a PushListener) and a running application.

Store a reference to an object for later use

For example, an application that allows a BlackBerry device user to add and remove an ApplicationMenuItem could use the runtime store to store a reference to an ApplicationMenuItem it has registered. After the application is closed and re-opened, the ApplicationMenuItem can be accessed and unregistered.

Implement system-wide singletons

An application might require one or more singleton objects to be accessed from within the application itself or by other applications.

Security of the runtime storeBy default, only BlackBerry® device applications that Research In Motion digitally signs can access data in the runtime store.

Restrict access to runtime store data using code signing keysCode signing keys can be used to control access to the runtime store. This is a way to restrict or share access with other apps on a BlackBerry® device.

1. Import the required classes and interfaces.

Development Guide Storing objects nonpersistently

47

Page 50: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

import java.util.Hashtable;import net.rim.device.api.system.RuntimeStore;

2. Create a hash ID for the object you want to store in the runtime store.

long MY_DATA_ID = 0x33abf322367f9018L;Hashtable myHashtable = new Hashtable();

3. Store the object in the runtime store and protect the object with the CodeSigningKey object. Only applications signed with the key can read or change the object.

RuntimeStore.put( MY_DATA_ID, new ControlledAccess( myHashtable, key ) );

4. Make sure that the object is protected with a particular code signing key, and invoke RuntimeStore.get, providing as parameters the hash ID for the object and the CodeSigningKey object.

Add an object to the runtime store1. Invoke RuntimeStore.put(long, String) and provide as parameters a unique long ID and the runtime

object to store.

2. Create a try/catch block to manage the IllegalArgumentException that put() throws if a runtime object with the same ID exists.

RuntimeStore store = RuntimeStore.getRuntimeStore();String msg = "Some shared text";long ID = 0x60ac754bc0867248L;try {store.put( ID, msg );} catch(IllegalArgumentException e) {}

Replace an object in the runtime store1. Invoke replace().

2. Create a try/catch block to manage the ControlledAccessException that replace() throws if the runtime object with the specified ID does not exist.

RuntimeStore store = RuntimeStore.getRuntimeStore();String newmsg = "Some new text";try {Object obj = store.replace( 0x60ac754bc0867248L, newmsg);} catch(ControlledAccessException e) {} not exist.

Retrieve the runtime store1. Import the required classes and interfaces.

Development Guide Add an object to the runtime store

48

Page 51: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

import java.lang.IllegalArgumentException;import java.lang.RuntimeException;import java.lang.String;import net.rim.device.api.system.ControlledAccessException;import net.rim.device.api.system.RuntimeStore;import net.rim.device.api.util.ListenerUtilities;

2. Invoke RuntimeStore.getRuntimeStore().

RuntimeStore store = RuntimeStore.getRuntimeStore();

Retrieve a registered runtime object1. Invoke RuntimeStore.get() and provide as a parameter the runtime object ID.

2. Create a try/catch block to manage the ControlledAccessException that get() throws if the application does not have read access to the specified runtime object.

RuntimeStore store = RuntimeStore.getRuntimeStore();try {Object obj = store.get(0x60ac754bc0867248L);} catch(ControlledAccessException e) {}

Retrieve an unregistered runtime object1. Invoke RuntimeStore.waitFor() to wait for registration of a runtime object to complete. If the runtime

object with the specified ID does not exist, waitFor() blocks for a maximum of MAX_WAIT_MILLIS.

2. Create code for handling exceptions.

RuntimeStore store = RuntimeStore.getRuntimeStore();try {Object obj = store.waitFor(0x60ac754bc0867248L);} catch(ControlledAccessException e) {} catch(RuntimeException e) {}

Code sample: Storing a String in the runtime storeFor simplicity, this example does not show how to create the unique ID.

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

Development Guide Retrieve a registered runtime object

49

Page 52: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

}

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

Code sample: Getting a stored String from the runtime storeFor simplicity, this example does not show how to create the unique ID.

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

Development Guide Code sample: Getting a stored String from the runtime store

50

Page 53: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

Code sample: Creating a singleton using the RuntimeStore APIThe 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 unique ID.

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;

}}

Development Guide Code sample: Creating a singleton using the RuntimeStore API

51

Page 54: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

Storing data in the record store 6The MIDP specification provides persistent storage for MIDlets. This mechanism is called the MIDP Record Management System (RMS), or record store. It is modeled after a simple record-oriented database. The record store is the MIDP equivalent of the RIM PersistentStore API and is available on all MIDP devices.

The RMS API is implemented in the javax.microedition.rms class.

While it is designed for MIDlets, the record store can also be used in BlackBerry® device applications.

The record store provides a simple record management system that allows you to create a data store object and persist a series of records within that object. Each record is a byte array, so you must serialize your data into a byte array format before storing it locally. 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. The RMS API does not provide any inherent indexing or relationships between records.

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

Data that an application saves in a record store is automatically deleted when the application is removed. When you upgrade an application that uses the record store, the data is retained.

Here are the maximum storage sizes for the record store:

BlackBerry® Device Software version Maximum individual record store size

Maximum total record store size (cumulative for all applications)

Earlier than 4.1 64 KB 64 KB4.1 to 4.5 64 KB Available device memory4.6 or later 512 KB Available device memory

Create a record store1. Import the javax.microedition.rms.RecordStore class.

2. Invoke openRecordStore(), and specify true to indicate that the method should create the record store if the record store does not exist.

RecordStore store = RecordStore.openRecordStore("Contacts", true);

Add a record to a record store1. Import the javax.microedition.rms.RecordStore class.

2. Invoke addRecord().

int id = store.addRecord(_data.getBytes(), 0, _data.length());

Development Guide Storing data in the record store

52

Page 55: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

Code sample: Adding a record to the record storeThe following code sample shows you how to add a byte array using the RMS API.

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

Retrieve a record from a record store1. Import the required classes and interfaces.

import java.lang.String;import javax.microedition.rms.RecordStore;

2. Invoke getRecord(int, byte[], int). Pass the following parameters:• a record ID• a byte array• an offset

byte[] data = new byte[store.getRecordSize(id)];store.getRecord(id, data, 0);String dataString = new String(data);

Retrieve all records from a record store1. Import the required classes and interfaces.

import javax.microedition.rms.RecordComparator;import javax.microedition.rms.RecordEnumeration;import javax.microedition.rms.RecordFilter;import javax.microedition.rms.RecordStore;

2. Invoke openRecordStore().

3. Invoke enumerateRecords(). Pass the following parameters:• filter: specifies a RecordFilter object to retrieve a subset of record store records (if null, the method

returns all records)• comparator: specifies a RecordComparator object to determine the order in which the method returns

the records (if null, the method returns the records in any order)• keepUpdated: determines if the method keeps the enumeration current with the changes to the record

store

Development Guide Retrieve a record from a record store

53

Page 56: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

RecordStore store = RecordStore.openRecordStore("Contacts", false);RecordEnumeration e = store.enumerateRecords(null, null, false);

Code sample: Storing and retrieving data with the record storeThis example uses the RMS API to store and retrieve high scores for a game. In the example, high scores are stored in separate records, and sorted when necessary using a RecordEnumeration.

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

/** * 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) {

Development Guide Code sample: Storing and retrieving data with the record store

54

Page 57: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

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() { //

Development Guide Code sample: Storing and retrieving data with the record store

55

Page 58: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

// 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();

Development Guide Code sample: Storing and retrieving data with the record store

56

Page 59: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

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) {

Development Guide Code sample: Storing and retrieving data with the record store

57

Page 60: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

System.out.println(rse); rse.printStackTrace(); } }

public static void main(String[] args) { RMSGameScores rmsgs = new RMSGameScores(); rmsgs.addScore(100, "Megan"); rmsgs.addScore(120, "Enrico"); rmsgs.addScore(80, "Andrea"); rmsgs.addScore(40, "Stephen"); rmsgs.addScore(200, "Sherisse"); rmsgs.addScore(110, "Acheson"); rmsgs.addScore(220, "Acheson"); System.out.println("All scores"); rmsgs.printScores(); System.out.println("Acheson's scores"); RMSGameScores.playerNameFilter = "Acheson"; rmsgs.printScores("Acheson"); }}

Development Guide Code sample: Storing and retrieving data with the record store

58

Page 61: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

Managing data 7The BlackBerry® Java® Virtual Machine manages memory usage on the BlackBerry device. The BlackBerry JVM allocates memory, performs garbage collection, and automatically swaps data between random access memory and application storage.

BlackBerry® Device Software provides a number of tools to help you manage data in your applications. These tools provide ways to back up data, remove sensitive data, remove data that is no longer needed, and free up memory when needed.

Best practice: Minimizing memory useTo minimize runtime memory, consider the following guidelines:

• Use primitive types (such as int or Boolean) instead of objects (such as String or Integer).• Do not depend entirely on the garbage collector.• Avoid creating many objects quickly.• Set object references to null when you are finished using them.• Reuse objects as much as possible.• Move heavy processing to the server. For example, you can filter or sort data before sending it to the BlackBerry®

device.

Removing sensitive dataThe memory cleaner can delete sensitive data that is stored in memory on a BlackBerry® device. Specific events trigger the memory cleaner to clear various caches and perform secure garbage collection. The memory cleaner is not on by default, but is turned on automatically when you enable encryption. To manually turn on the memory cleaner, the device user clicks Options > Security Options > Advanced Security Options > Memory Cleaning and sets the status to Enabled.

The Memory Cleaner API is implemented in the net.rim.device.api.memorycleaner package.

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 MemoryCleanerListener interface has two methods, cleanNow() and getDescription(). The cleanNow() method is invoked 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 the memory cleaner must display information about the applications that are registered cleaners. This functionality is required, for example, on the Memory Cleaning option screen.

Development Guide Managing data

59

Page 62: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

Using the Garbage CollectorThe BlackBerry® Java® Virtual Machine includes the Garbage Collector, which runs periodically to remove unreferenced and weakly referenced objects from memory. Do not call the Garbage Collector directly, but release resources by setting their reference to null after use.

Full garbage collection on a BlackBerry deviceThe full garbage collection operation executes for 1 second on average and should take less than 2 seconds to complete. The full garbage collection operation performs the following actions:

• It performs a RAM garbage collection operation.• It marks objects in flash memory that are no longer referenced or no longer persisted.• It releases any nonpersistent object handles in RAM and flash memory.

The system might initiate a full garbage collection operation in the following situations:

• The BlackBerry® Java® Virtual Machine cannot allocate an object because of a lack of available space in RAM.• A process is about to exceed its currently allocated heap size.• The BlackBerry JVM cannot allocate a new object because the object handles are not available.• The BlackBerry device is idle.

RAM garbage collection on a BlackBerry device

The BlackBerry® Java® Virtual Machine initiates a RAM garbage collection operation only when the BlackBerry JVM cannot allocate an object because of a lack of space in RAM. The RAM garbage collection operation typically takes 500 to 600 milliseconds to execute. The garbage collection operation removes any freshly allocated variables that are no longer referenced in RAM. To make sure that the lack of a reference in RAM is a sufficient condition for removing the object, a RAM garbage collection operation can only be performed when objects have not been paged out to flash memory.

Idle garbage collection on a BlackBerry device

Garbage collection does not occur every time the BlackBerry® device idles. It occurs only when the system considers a garbage collection operation to be beneficial for optimal system performance and maximized battery performance.

To improve performance without impacting the BlackBerry device user experience, the system attempts to perform the following garbage collection operations when the BlackBerry device idles:

• A full garbage collection operation can occur when the BlackBerry device idles for a relatively short period of time.

• A thorough garbage collection operation can occur when the BlackBerry device idles for a significant period of time.

Development Guide Using the Garbage Collector

60

Page 63: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

Managing low memoryWhen the available memory on a BlackBerry® device falls below the threshold that the device requires to function correctly, the Low Memory Manager attempts to make more memory available. The Low Memory Manager prioritizes objects in memory and marks the less critical objects for deletion by the BlackBerry® Java® Virtual Machine. Opened messages and older calendar entries are typically deleted first.

The Low Memory Manager API is implemented in net.rim.device.api.lowmemory .

You should design your application to work with the Low Memory Manager 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 Low Memory Manager by calling the static LowMemoryManager.addLowMemoryListener() method.

The LowMemoryListener interface has a single method, freeStaleObject(), that is invoked by the Low Memory Manager when it needs to make memory available. When it invokes freeStaleObject(), the Low Memory Manager 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 Low Memory Manager needs an accurate accounting of the memory freeing progress.

Identifying low memory availability on a BlackBerry deviceThe following conditions can cause the Low Memory Manager to attempt to free memory resources:

• The amount of available memory on the BlackBerry® device falls below a certain threshold. The threshold depends on the amount of free RAM in the system. The memory threshold ranges from 400 KB to 800 KB.

• The number of persistent object handles that are available on the BlackBerry device falls below 1000.• The number of object handles that are available on the BlackBerry device falls below 1000.

Backing up dataThe BlackBerry® Device Manager provides a backup and restore tool that a BlackBerry device user can use to save BlackBerry device data to a file on a computer and to restore data to the BlackBerry device.

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.

The Synchronization API is implemented in the net.rim.device.api.synchronization package.

When an application uses the Synchronization API, the BlackBerry Desktop Manager can back up and restore the application database at the same time as other BlackBerry device databases. You can use the Synchronization API to create data archives or to populate application databases the first time the BlackBerry device connects to the BlackBerry device user's computer.

Development Guide Managing low memory

61

Page 64: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

To synchronize data to remote data sources, you must build the synchronization logic into your application. Most applications send data to a server-side application using standard HTTP or TCP/IP protocols over the wireless network and the Internet or an intranet. You can use XML APIs to generate and parse XML-formatted data to send and receive over the wireless network. However, your client-side and server-side applications must read and write the data properly and acknowledge successful transmission of the data.

Your BlackBerry device application might connect to an application on a computer to send the data over a USB connection with the Synchronization API and the BlackBerry Desktop Manager. In this case, the desktop application must be able to read the data from your BlackBerry device application through an add-in task for the BlackBerry Desktop Manager. The BlackBerry device user must manually execute the synchronization by running the BlackBerry Desktop Manager add-in, which notifies the application on the BlackBerry device to send the data to the computer application. You can also write data to the desktop application using the native USB protocols.

To enable an application to back up data, you can 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.

The following sample applications are included with the BlackBerry® Java® Development Environment: SyncDemo, OTASyncDemo, and OTABackupRestoreDemo.

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. For more information, see Backup and restore small amounts of data using SyncItem.

Development Guide Backing up data

62

Page 65: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

Find more information 8• www.blackberry.com/go/apiref: View the latest version of the API reference for the BlackBerry® Java® SDK.• www.blackberry.com/go/devguides: Find development guides, release notes, and sample application overviews

for the BlackBerry Java SDK.• www.blackberry.com/developers: Visit the BlackBerry® Developer Zone for resources on developing BlackBerry

device applications.• www.blackberry.com/go/developerkb: View knowledge base articles on the BlackBerry Development

Knowledge Base.• www.blackberry.com/developers/downloads: Find the latest development tools and downloads for developing

BlackBerry device applications.

Development Guide Find more information

63

Page 66: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

Provide feedback 9To provide feedback on this deliverable, visit www.blackberry.com/docsfeedback.

Development Guide Provide feedback

64

Page 67: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

Glossary 10application storage

Application storage is internal to the BlackBerry device. It contains the operating system, BlackBerry® JVM, and an internal file system. Application storage is also known as flash memory and onboard memory. Applications on a BlackBerry device can be run only from the application storage. All BlackBerry devices have application storage.

built-in media storageBuilt-in media storage is a storage location on an eMMC. It is not removable. A FAT file system is mounted on the built-in media card. Built-in media storage is also called internal media memory and onboard device memory.

DUCETDefault Unicode Collation Element Table

eMMCembedded MultiMediaCard

FATFile Allocation Table

flash memoryThe flash memory is an internal file system on a BlackBerry device that stores application data and user data.

JSRJava® Specification Request

media card storageMedia card storage is a storage location on a microSD card that a BlackBerry device user can use to extend the amount of storage on a device. The media card storage is optional and removable. A FAT file system is mounted on the media card.

MIDPMobile Information Device Profile

persistent store in flash memoryThe persistent store in flash memory stores data for a BlackBerry device. By default, third-party applications cannot access the persistent store. When it deletes all device data, the BlackBerry device deletes the data in the persistent store.

RIM signing authority systemThe RIM® signing authority system is a collection of servers that sign the boot ROM code for a BlackBerry device during the manufacturing process.

Development Guide Glossary

65

Page 68: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

SQLStructured Query Language

UTF-88-bit UCS/Unicode Transformation Format

Development Guide Glossary

66

Page 69: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

Document revision history 11

Date Description

30 May 2011 The chapter "Storing data in SQLite® databases" was rewritten to accommodate new features and behavior changes in BlackBerry® Java® SDK 7.0

22 February 2011 Deleted topic: Remove persistent data29 November 2010 Added topic: Using foreign key constraints26 November 2010 Widespread edits16 August 2010 Initial version

Development Guide Document revision history

67

Page 70: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

Legal notice 12©2011 Research In Motion Limited. All rights reserved. BlackBerry®, RIM®, Research In Motion®, and related trademarks, names, and logos are the property of Research In Motion Limited and are registered and/or used in the U.S. and countries around the world.

Java is a trademark of Oracle America, Inc. SQLite is a trademark of Hipp, Wyrick & Company, Inc. All other trademarks are the property of their respective owners.

This documentation including all documentation incorporated by reference herein such as documentation provided or made available at www.blackberry.com/go/docs is provided or made accessible "AS IS" and "AS AVAILABLE" and without condition, endorsement, guarantee, representation, or warranty of any kind by Research In Motion Limited and its affiliated companies ("RIM") and RIM assumes no responsibility for any typographical, technical, or other inaccuracies, errors, or omissions in this documentation. In order to protect RIM proprietary and confidential information and/or trade secrets, this documentation may describe some aspects of RIM technology in generalized terms. RIM reserves the right to periodically change information that is contained in this documentation; however, RIM makes no commitment to provide any such changes, updates, enhancements, or other additions to this documentation to you in a timely manner or at all.

This documentation might contain references to third-party sources of information, hardware or software, products or services including components and content such as content protected by copyright and/or third-party web sites (collectively the "Third Party Products and Services"). RIM does not control, and is not responsible for, any Third Party Products and Services including, without limitation the content, accuracy, copyright compliance, compatibility, performance, trustworthiness, legality, decency, links, or any other aspect of Third Party Products and Services. The inclusion of a reference to Third Party Products and Services in this documentation does not imply endorsement by RIM of the Third Party Products and Services or the third party in any way.

EXCEPT TO THE EXTENT SPECIFICALLY PROHIBITED BY APPLICABLE LAW IN YOUR JURISDICTION, ALL CONDITIONS, ENDORSEMENTS, GUARANTEES, REPRESENTATIONS, OR WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY CONDITIONS, ENDORSEMENTS, GUARANTEES, REPRESENTATIONS OR WARRANTIES OF DURABILITY, FITNESS FOR A PARTICULAR PURPOSE OR USE, MERCHANTABILITY, MERCHANTABLE QUALITY, NON-INFRINGEMENT, SATISFACTORY QUALITY, OR TITLE, OR ARISING FROM A STATUTE OR CUSTOM OR A COURSE OF DEALING OR USAGE OF TRADE, OR RELATED TO THE DOCUMENTATION OR ITS USE, OR PERFORMANCE OR NON-PERFORMANCE OF ANY SOFTWARE, HARDWARE, SERVICE, OR ANY THIRD PARTY PRODUCTS AND SERVICES REFERENCED HEREIN, ARE HEREBY EXCLUDED. YOU MAY ALSO HAVE OTHER RIGHTS THAT VARY BY STATE OR PROVINCE. SOME JURISDICTIONS MAY NOT ALLOW THE EXCLUSION OR LIMITATION OF IMPLIED WARRANTIES AND CONDITIONS. TO THE EXTENT PERMITTED BY LAW, ANY IMPLIED WARRANTIES OR CONDITIONS RELATING TO THE DOCUMENTATION TO THE EXTENT THEY CANNOT BE EXCLUDED AS SET OUT ABOVE, BUT CAN BE LIMITED, ARE HEREBY LIMITED TO NINETY (90) DAYS FROM THE DATE YOU FIRST ACQUIRED THE DOCUMENTATION OR THE ITEM THAT IS THE SUBJECT OF THE CLAIM.

TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW IN YOUR JURISDICTION, IN NO EVENT SHALL RIM BE LIABLE FOR ANY TYPE OF DAMAGES RELATED TO THIS DOCUMENTATION OR ITS USE, OR PERFORMANCE OR NON-PERFORMANCE OF ANY SOFTWARE, HARDWARE, SERVICE, OR ANY THIRD PARTY PRODUCTS AND SERVICES REFERENCED HEREIN INCLUDING WITHOUT LIMITATION ANY OF THE FOLLOWING DAMAGES: DIRECT, CONSEQUENTIAL, EXEMPLARY, INCIDENTAL, INDIRECT, SPECIAL, PUNITIVE, OR AGGRAVATED DAMAGES, DAMAGES

Development Guide Legal notice

68

Page 71: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

FOR LOSS OF PROFITS OR REVENUES, FAILURE TO REALIZE ANY EXPECTED SAVINGS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, LOSS OF BUSINESS OPPORTUNITY, OR CORRUPTION OR LOSS OF DATA, FAILURES TO TRANSMIT OR RECEIVE ANY DATA, PROBLEMS ASSOCIATED WITH ANY APPLICATIONS USED IN CONJUNCTION WITH RIM PRODUCTS OR SERVICES, DOWNTIME COSTS, LOSS OF THE USE OF RIM PRODUCTS OR SERVICES OR ANY PORTION THEREOF OR OF ANY AIRTIME SERVICES, COST OF SUBSTITUTE GOODS, COSTS OF COVER, FACILITIES OR SERVICES, COST OF CAPITAL, OR OTHER SIMILAR PECUNIARY LOSSES, WHETHER OR NOT SUCH DAMAGES WERE FORESEEN OR UNFORESEEN, AND EVEN IF RIM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW IN YOUR JURISDICTION, RIM SHALL HAVE NO OTHER OBLIGATION, DUTY, OR LIABILITY WHATSOEVER IN CONTRACT, TORT, OR OTHERWISE TO YOU INCLUDING ANY LIABILITY FOR NEGLIGENCE OR STRICT LIABILITY.

THE LIMITATIONS, EXCLUSIONS, AND DISCLAIMERS HEREIN SHALL APPLY: (A) IRRESPECTIVE OF THE NATURE OF THE CAUSE OF ACTION, DEMAND, OR ACTION BY YOU INCLUDING BUT NOT LIMITED TO BREACH OF CONTRACT, NEGLIGENCE, TORT, STRICT LIABILITY OR ANY OTHER LEGAL THEORY AND SHALL SURVIVE A FUNDAMENTAL BREACH OR BREACHES OR THE FAILURE OF THE ESSENTIAL PURPOSE OF THIS AGREEMENT OR OF ANY REMEDY CONTAINED HEREIN; AND (B) TO RIM AND ITS AFFILIATED COMPANIES, THEIR SUCCESSORS, ASSIGNS, AGENTS, SUPPLIERS (INCLUDING AIRTIME SERVICE PROVIDERS), AUTHORIZED RIM DISTRIBUTORS (ALSO INCLUDING AIRTIME SERVICE PROVIDERS) AND THEIR RESPECTIVE DIRECTORS, EMPLOYEES, AND INDEPENDENT CONTRACTORS.

IN ADDITION TO THE LIMITATIONS AND EXCLUSIONS SET OUT ABOVE, IN NO EVENT SHALL ANY DIRECTOR, EMPLOYEE, AGENT, DISTRIBUTOR, SUPPLIER, INDEPENDENT CONTRACTOR OF RIM OR ANY AFFILIATES OF RIM HAVE ANY LIABILITY ARISING FROM OR RELATED TO THE DOCUMENTATION.

Prior to subscribing for, installing, or using any Third Party Products and Services, it is your responsibility to ensure that your airtime service provider has agreed to support all of their features. Some airtime service providers might not offer Internet browsing functionality with a subscription to the BlackBerry® Internet Service. Check with your service provider for availability, roaming arrangements, service plans and features. Installation or use of Third Party Products and Services with RIM's products and services may require one or more patent, trademark, copyright, or other licenses in order to avoid infringement or violation of third party rights. You are solely responsible for determining whether to use Third Party Products and Services and if any third party licenses are required to do so. If required you are responsible for acquiring them. You should not install or use Third Party Products and Services until all necessary licenses have been acquired. Any Third Party Products and Services that are provided with RIM's products and services are provided as a convenience to you and are provided "AS IS" with no express or implied conditions, endorsements, guarantees, representations, or warranties of any kind by RIM and RIM assumes no liability whatsoever, in relation thereto. Your use of Third Party Products and Services shall be governed by and subject to you agreeing to the terms of separate licenses and other agreements applicable thereto with third parties, except to the extent expressly covered by a license or other agreement with RIM.

Certain features outlined in this documentation require a minimum version of BlackBerry® Enterprise Server, BlackBerry® Desktop Software, and/or BlackBerry® Device Software.

Development Guide Legal notice

69

Page 72: Blackberry Java SDK Development Guide 1232721 0730084309 001 6.0 US

The terms of use of any RIM product or service are set out in a separate license or other agreement with RIM applicable thereto. NOTHING IN THIS DOCUMENTATION IS INTENDED TO SUPERSEDE ANY EXPRESS WRITTEN AGREEMENTS OR WARRANTIES PROVIDED BY RIM FOR PORTIONS OF ANY RIM PRODUCT OR SERVICE OTHER THAN THIS DOCUMENTATION.

Research In Motion Limited295 Phillip StreetWaterloo, ON N2L 3W8Canada

Research In Motion UK Limited Centrum House 36 Station Road Egham, Surrey TW20 9LF United Kingdom

Published in Canada

Development Guide Legal notice

70