370 likes | 506 Views
Cosc 4730. Blackberry: Record Store & SQLite. Introduction. RecordStore Comes from JavaME , MIDP 1.0 On some phone who where you may not have a filesystem access and RecordStore was only way to store persistent data Falls very short of a Database
E N D
Cosc4730 Blackberry: Record Store & SQLite
Introduction • RecordStore • Comes from JavaME, MIDP 1.0 • On some phone who where you may not have a filesystem access and RecordStore was only way to store persistent data • Falls very short of a Database • While this lecture will say MIDlet, it’s the same for a Blackberry Application as well. • SQLite • Introduces into Blackberry APIs at 5.0.0
Record Store (2) • The record store is stored in a non-volatile place and will remain after the MIDlet/application has exited. • The stored data by default is marked private, meaning only that MIDlet can access the data • Can be set as shared, allowing any MIDlet to access it. • When the MIDlet suite is removed, all record stores (thus data stored in the Record Stores) that the MIDlet created are also removed.
Record Store (3) • Record store names must be unique • The name of the MIDlet is also used as part of the name • You only need to worry about uniqueness, if you application has 2 or more Record Stores (think Database and table names here). • The name can be 32 Unicode strings (for localization) and it is case sensitive • A note, the name of a Record store is not visual to the users.
Record Store (4) • Records are within the Record Store are given a unique index, which the primary key, called recordId. • The index starts with 1, the next record will be 2, etc… • When a record is deleted from the Record store, the index is NOT reused. It's set to null. • Using RecordEnumeration other indices can be created. • Records stores also store more information • a time stamped when the record store was last modified • (in long integers), in the format used by System.currentTimeMillis(); • a version, which is an integer incremented for each operation that modifies the contests of the Record Store. • Useful for synchronization engines and other things.
Record Store (5) • Implementations of Record Store must guarantee atomic, synchronous, and serialized actions across a single thread, so no corruption will occur. • No locking operations provided • With multiple threads, it is the MIDlet's (programmers) responsibility. • Reader/writer problems.
Using Record Store • While the code to follow will get complex, there is very little you can do • Open/close a record store • store a record in the record store • read a record from the record store • delete the record store. • get the "version" and data store of the record store. • uses RecordStore class from the javax.microedition.rmspackage.
Open/create a RecordStore • openRecordStore(…) • Open or possibly create a record store associated with MIDlet suite. • Three versions: (we'll come back to the third later) • static RecordStoreopenRecordStore(String recordStoreName, booleancreateIfNecessary) • Name of the RecordStore and True/False create it if doesn't exist. Created as a private Record Store. • static RecordStoreopenRecordStore(String recordStoreName, booleancreateIfNecessary, intauthmode, boolean writable) • authmode: • AUTHMODE_ANY any MIDlet suites can access it • AUTHMODE_PRIVATE only access from current MIDlet suite
Open/create a RecordStore (2) • Throws the following errors: • RecordStoreException • if a record store-related exception occurred • RecordStoreNotFoundException • if the record store could not be found • RecordStoreFullException • if the operation cannot be completed because the record store is full • IllegalArgumentException • if recordStoreName or authmode is invalid
openRecordStore Example import javax.microedition.rms.*; private RecordStorers = null; try { rs = RecordStore.openRecordStore("MyStore", true); } catch (RecordStoreException ex) { //do something. } • OR rs = RecordStore.openRecordStore("MyStore", true, RecordStore.AUTHMODE_ANY, true); //Allow any MIDlet to access it, and the last true is for writeable. • Now we use rs to read/write/whatever to the RecordStore called "MyStore", which was created if it did not already exist.
Open/create a RecordStore (3) • Third version is a little different. • static RecordStoreopenRecordStore(String recordStoreName, String vendorName, String suiteName) • Used to open other RecordStore created by other MIDlet suites. • Note that RecordStore must be AUTHMODE_ANY to succeed. • VendorName and suiteName are set in Application descriptor properties. • Throws same errors and adds the following: • SecurityException • if this MIDlet Suite is not allowed to open the specified RecordStore.
Closing a RecordStore • rs.closeRecordStore(); • You need to close the RecordStore to ensure the resources are returned. • A note, it would seem you can open a RecordStore multiple times and you need to close it the same number of times as well, otherwise it won't be closed correctly. • Throws • RecordStoreNotOpenException • if the record store is not open • RecordStoreException • if a different record store-related exception occurred
Delete a Record Store • If you need to delete a RecordStore use • deleteRecordStore(String recordStoreName) • Throws: • RecordStoreException • if a record store-related exception occurred • RecordStoreNotFoundException • if the record store could not be found • Remember a RecordStore is also removed when the MIDlet is removed.
Data storage. • Since there is no serialization in JavaMe, we must do the work. • Data in a record is stored as bytes, instead of any type (ie string or integer). • Like in filesystems, we’ll use DataOutputStream and DataInputStream to do the heavy lifting.
Data storage (2) • You will need to think about how you store the data, since you will also have to convert it back as well. • This is one of two places recordstore falls very short of databases • Recordstore looks like this table.
String methods for bytes • You may find it easier to just create a String that is the information, instead of stream. • Example: • high score data. We need to store Name and score. • A string could be created as "Jim Ward, 3012" • String str = "Jim Ward, 3012"; • byte bytes[] = str. getBytes(); • bytes to string is as simple • String newstr = new String(bytes,0,bytes.length); • Then use the string methods to break up the string into the necessary parts.
adding a record • To insert a new record • intaddRecord(byte[] data, int offset, intnumBytes) • Where offset is the index to the first relevant byte • Normally zero for us purposes. • numBytes is the length • returns the recordId number • Throws • RecordStoreNotOpenException • if the record store is not open • RecordStoreException • if a different record store-related exception occurred • RecordStoreFullException • if the operation cannot be completed because the record store has no more room • SecurityException • if the MIDlet has read-only access to the RecordStore
adding a record example //data were want to insert String newdata = "Jim Ward, 3012"; //byte data variable byte bytes[] = newdata.getBytes(); intrsId; try rsId = rs.addRecord(bytes,0,bytes.length); } catch (RecordStoreException ex) { //do something about failure. }
retrieving a record • The second place RecordStore fails database methods. • To get retrieve data, you need to know the index. • byte[] getRecord(intrecordId) • Returns a copy of the data stored in the given record. • throws • RecordStoreNotOpenException • if the record store is not open • InvalidRecordIDException • if the recordId is invalid • RecordStoreException • if a general record store exception occurs
retrieving a record (2) • There is no "select" method as you would think in a database. • Instead we use enumerateRecords method • We'll come back to it later on.
retrieving a record example byte b[]=null; try { b = rs.getRecord(rsId); } catch (RecordStoreException ex) { //do something about failure } String str = new String(b,0,b.length); //str should have "Jim Ward, 3012"
Updating a record • To change a record, use setRecord Method, which is the same as addRecord, except you specify the recordId as well. • setRecord(intrecordId, byte[] newData, int offset, intnumBytes) • throws • RecordStoreNotOpenException • if the record store is not open • InvalidRecordIDException • if the recordId is invalid • RecordStoreException • if a general record store exception occurs • RecordStoreFullException • if the operation cannot be completed because the record store has no more room • SecurityException • if the MIDlet has read-only access to the RecordStore
deleting a record • deleteRecord(intrecordId) • The record is deleted from the record store. • Remember, index are never reused, instead that index will be set to null • it will return either null or throw an exception if you attempt to access it again.
Counting Records • getNumRecords() • Returns the number of records currently in the record store. • Throws: • RecordStoreNotOpenException • if the record store is not open • Remember that since recordID, which are deleted are still "in use", it will count those as well.
Enumerating a Record Store • Because everything is stored as bytes, there is no "simple" way to implement something like database a select statement. • Instead use the enumerateRecords method, which returns an object of type RecordEnumeration. • RecordEnumerationenumerateRecords( RecordFilter filter, RecordComparator comparator, booleankeepUpdated) • throws RecordStoreNotOpenException • Where both filter and comparator are methods • filter used to determine the subset of records • comparator used to determine the order of the records. • keepUpdated, true watches RecordStore updates and then adds them.
Enumerating a Record Store (2) • If we wanted to return all the records, with no ordering, then will use the following: try { RecordEnumeration re = rs.enumerateRecords(null, null, false); } catch (RecordStoreNotOpenException ex) { //failed, RecordStore is not opened. } //now re has access to all the records. //we'll come back to RecordEnumeration class.
Enumerating a Record Store (3) • Let's say we want to see only a subset of records, which starts with "Jim", with no ordering. RecordEnumeration re = rs.enumerateRecords( new RecordFilter() { //required method match. public boolean match (byte[] r) { String str = new String(r,0,r.length); if (str.startsWith("Jim") { return true; //in the subset } else { return false; //not in the subset } } }, null, false);
Enumerating a Record Store (4) • Let's say we want to see all the records, but order them in alphabetic order. RecordEnumeration re = rs.enumerateRecords(null, new RecordComparator() { //compare is required public int compare ( byte[] r1, byte[] r2) { String str1 = new String(r1,0,r1.length); String str2 = new String(r2,0,r2.length); int x = str1.compareTo(str2); if (x <0) { return RecordComparator.PRECEDES; } } else if (x == 0) {return RecordComparator.EQUIVALENT; } } else {return RecordComparator.FOLLOWS; } }, false);
RecordEnumeration class • So now we have the records, use the RecordEnumeration methods to access them. • Use byte[] nextRecord() to get the record • call again, to get the next record. • to go back, use byte[] previousRecord(); • To test if there is a next or previous record • booleanhasNextElement() and booleanhasPreviousElement() • To get the RecordId, use nextRecordId() //as defined by the current interator, IE call this first if you want the RecordID and then the data. • previousRecordID() if you already called nextRecord() or to find out the last recordID. • A note, nextRecordID also mores the iterator. • intnumRecords() is useful to found how many records were selected. • And very importantly, when we are done. Call destory(), so it can free up any resources in use.
RecordEnumeration class example • So let's assume we wanted all records starting with Jim and they are in alphabetic order and we want to print them out. • Use the code from Enumerating a Record Store (3 and 4). • Now we have re as a variable and assume bytes and str are declared as before. while (re.hasNextElement()) { bytes = re.nextRecord(); str = new String(bytes,0,bytes.length); System.out.println("Record is "+str); } re.destroy(); • later close the RecordStore as well.
Listeners • An app can listen for changes in RecordStore caused by another application or thread. • You can addRecordListener(RecordListener l) the following: • void recordAdded(RecordStorerecordStore, intrecordId) • Called when a record has been added to a record store. • void recordChanged(RecordStorerecordStore, intrecordId) • Called after a record in a record store has been changed. • void recordDeleted(RecordStorerecordStore, intrecordId) • Called after a record has been deleted from a record store.
Other info about a RecordStore • If you don't know the names of RecordStore, you can get a list of all RecordStore for the MIDlet suite • String[] listRecordStores() • returns a string array of the names, null if no RecordStores are found. • Changing the Mode of the RecordStore • rs.setMode(intauthmode, boolean writable)
Other info about a RecordStore (2) • To get the last time the record store was modified • long x = rs.getLastModified() • returns in long int, formt used by System.currentTimeMillis() • time in non-leap seconds since January 1, 1970 • get the Version number • the Version number that indicates the number of changes (by addRecord, setRecord, and deleteRecord). But since the start number is implementation-specific, so you can't count the number of changes, since it was created. • int x = rs.getVersion(); • Can be easy used in multi threads MIDlet's to determine if the data may have changed since the read.
Other info about a RecordStore (3) • Size information • int x = rs.getRecordSize(intrecordId) • Returns the size (in bytes) of the MIDlet data available in the given record. • int x = rs.getSize() • Returns the amount of space, in bytes, that the record store occupies. • int x = getSizeAvailable() • Returns the amount of additional room (in bytes) available for this record store to grow.
Other info about a RecordStore (4) • int x = getNextRecordID(); • Returns the recordId of the next record to be added to the record store. • Maybe useful if you want to add some sort of relational information in records. • example: recordID 1 has index as part of the record 5, where recordID 5 haves addition information.
Limitations • Because of different implementations on different devices, you may encounter the following • Just because a device has a certain amount of space on the device, doesn't mean you can use all of it. • Some implementations of getSizeAvailable report all the space in persistent storage, not just what you can use. • The limitation of the bytes of a record. • It maybe implemented to a different size on different devices. • There is no way to predict or find the cap. Instead an error will be thrown when it is to long. • Blackberry Doc’s say 512K is the length.
Q A &