4. Persistent Storage Basics
• Simple record-oriented database (RMS) stored
in Flash mem
• Device-independent API
• Records are arrays of bytes that live in record
stores
• Record stores are shared within MIDlet suite
– MIDP 2.0 allows for optional sharing of record
stores between MIDlet suites
• Support for enumeration, sorting, and filtering
5. RMS Classes and Interfaces
• Defined in javax.microedition.rms package
• RecordStore
– Collection of records
• RecordEnumerator
– RecordStore enumerator
• Interfaces
– RecordComparator
– RecordFilter
– RecordListener
6. J2ME
Using Recordsint id
RecordStore
byte[] data
int id
byte[] data
int id
byte[] data
Old
RecordStore
• Nothing fancy here
– Array of bytes
• Integer values used a unique ID
• Records can be written using java.io API’s in
CLDC support:
– DataInputStream DataOutputStream
– ByteArrayInputStream ByteArrayOutputStream
7. RecordStore Operations
• Standard operations you would expect:
– openRecordStore(name,create)
– removeRecordStore(name)
• Get list of all known record stores in a MIDlet
suite
– listRecordStores()
• Get the size of the RS
– int getSize() - # of bytes used by the RS
– Int getSizeAvailable() – get amount of space
available for both record data and overhead
8. Creating a RecordStore
• Centered around record stores
– Small database that contains data called records
• javax.microedition.rms.RecordStore
• Record stores are identified by name
public static final String RS_NAME = "Tasks";
try {
m_RS =
RecordStore.openRecordStore(RS_NAME, bCreateIfNece
ssary);
}
catch (RecordStoreException ex) {
this.m_bRecordStoreError = true;
9. Closing/Deleting
RecordStore
• Need to make sure that you release the
resource
protected void destroyApp(boolean parm1) throws
javax.microedition.midlet.
MIDletStateChangeException {
try {
m_RS.closeRecordStore();
RecordStore.deleteRecordStore(RS_NAME);
}
catch (RecordStoreException ex) {
ex.printStackTrace();
System.out.println(ex.getMessage());
}
10. RMSMidlet
•
Sample MIDlet so that you can become familiar with RMS
and take a little tour.
– com.sb.samples.midlet.rms package
•
1.
2.
3.
4.
Our objective is to introduce RMS basics including:
RecordStore creation/closing
Record adding
Record lookup filtering via RecordFilter
RecordStore deletion (no deletions take place
though very easy to add)
5. Encapsulating RecordStore I/O operations in
static Utility Class methods for consistency
11. RMSMidlet Description
• Very simple
• Creates 5 TaskRow objects.
• Only object 3 has been designated as 'already
uploaded to server'.
• Shows a single form with only those TaskRows
that have not been uploaded to the server
(there are 4 of them in this case).
• On the form is the Record ID of the Task
• User can press 'View' button to see Task
12. TaskRow Class
• com.sb.samples.midlet.rms.TaskRow class that
acts as:
– Container object for Task values
– Decouples Task data from RecordStore
representation
– Static utility tool for consistent, maintainable
TaskRow-to-RecordStore I/O
13. Adding Records
• TaskRow offers a number of ways to add, read,
and update a record
public static int addRecord(RecordStore rs, TaskRow
taskRow) throws Exception {
byte[] bData = TaskRow.toByteArray(taskRow);
int iRecordID = rs.addRecord(bData, 0,
bData.length);
return iRecordID;
}
public static void updateRecord(RecordStore rs,
TaskRow taskRow) throws Exception {
byte[] bData = TaskRow.toByteArray(taskRow);
rs.setRecord(taskRow.iID, bData, 0,
bData.length);
}
14. Read Record
public static TaskRow readRecord(RecordStore rs, int iRecordID) throws Exception {
byte[] bData = rs.getRecord(iRecordID);
TaskRow tr = readRecord(bData);
tr.iID = iRecordID;
return tr;
}
// Maintain a single, consistent InputStream-based read mechanism
// for use by readRecord and any other usage
public static TaskRow readRecord(DataInputStream dis) throws Exception {
TaskRow tr = new TaskRow();
// the order of these reads must match
// the reads in TaskRow.toByteArray() with respect to:
// 1) the ordering of the values
// 2) the data type of each value
tr.iRadioID = dis.readInt();
tr.bUploaded = dis.readBoolean();
tr.sTask = dis.readUTF();
tr.sLongitude = dis.readUTF();
tr.sLatitude = dis.readUTF();
tr.sStatus = dis.readUTF();
return tr;
}
15. Read Record – Byte Array
// Offer a Byte Array-based reader for parts of the program that
// do not have a reference to RecordStore
// (e.g. by RecordFilter or RecordComparator that receive only
byte arrays)
public static TaskRow readRecord(byte[] bData) throws Exception {
ByteArrayInputStream bais = new ByteArrayInputStream(bData);
DataInputStream dis = new DataInputStream(bais);
try {
return readRecord(dis);
} finally {
if (null != dis){
try {
dis.close();
} catch (Exception e){
}
}
}
}
16. Lookup Filtering
• The RMSMidlet keeps track of records that
have not been uploaded to a server.
• And then we use a Filter to determine which
records need to be uploaded.
17. Filter for Records
protected TaskRow[] getUnuploadedTaskRows() throws Exception {
// Filter for tasks not yet uploaded to server
RecordFilter rf = new RecordFilterTaskNotUploaded();
// If we cared about ordering (e.g. Data created), we'd
// create a RecordComparator for that purpose. For now, don't sort.
RecordComparator nullRecordComparator = null;
boolean bKeepUpdated = false;
RecordEnumeration re = m_RS.enumerateRecords(rf,nullRecordComparator,
bKeepUpdated);
TaskRow[] aTaskRows = new TaskRow[re.numRecords()];
int i = 0;
while (re.hasNextElement()) {
int iNextRecord = 0;
try {
iNextRecord = re.nextRecordId();
TaskRow tr = TaskRow.readRecord(m_RS, iNextRecord);
aTaskRows[i] = tr;
i++;
}
catch (Exception ex) {
throw ex;
}
18. RecordFilterTaskNotUpload
ed Class
public class RecordFilterTaskNotUploaded
implements RecordFilter {
// only allow TaskRows that have not been uploaded to
// be accepted
public boolean matches(byte[] bCandidate) {
try {
TaskRow tr = TaskRow.readRecord(bCandidate);
if (tr.bUploaded == false) {
return true;
}
else {
return false;
}
}
catch (Exception e) {
return false;
}
19. RMS Tour
• That concludes our RMS tour.
• Unlike most tours, our RMS tour doesn’t end
at a gift shop…
21. J2ME and J2EE
• MIDP App’s become a client with Middle-tier
access
• XML data provided by a Servlet at well-known
URL or through Web Services
• Received as streaming data using networking
API’s
• Converted to String for parsing (i.e. kXML)
• Display using MIDP UI elements
24. MIDP & XML
• MIDP networking allows access to data
formats like WML, XML
– We’re just interested in XML
• Upside
– Easy to work with
– Most are familiar with XML
• Downside
– Expensive
– Heavy String manipulation
– Footprint for the parser
• For some devices, not as big a deal because
25. MIDP XML Parsers
• A few parsers are available for MIDP
• kXML
– http://www.kxml.org
– Pull parser
– SAX and DOM support
– Written for J2ME/CLDC/MIDP
• NanoXml
– http://nanoxml.sourceforge.net/kvm.html
– DOM
– Ported
26. kXML-RPC
• kXML-RPC is a J2ME implementation of the
XML-RPC protocol built on top of the kXML
parser.
• Extremely lightweight mechanism for
exchanging data and invoking web services in
a neutral, standardized XML format.
• Hides the implementation of kXML
27. Dealing with Payloads
• Not really all that much to do here when using
kXML-RPC, the payload is handled for you on
the client, and the server uses a TaskHandler
to get the parameters
• Let’s look at making a call, and then a sample
of what a payload might look like if you
wanted to sniff the wire…
28. Making an XML-RPC Call
• Using XML-RPC is as simple as doing the
following:
String connect =
“http://www.switchbacksoftware.com/service.do”;
String ticker = "Submitting to Server";
updateUI();
XmlRpcClient xmlrpc = new XmlRpcClient(connect);
try {
String result = (String)
xmlrpc.execute(SB.submitTaskInfo, paramsArray);
30. J2ME Web Services API (WSA) JSR-172 (CLDC 1.0 & 1.1) - API's
standardize remote service invocation and XML parsing - subsets
of based on JAX-RPC 1.1 and SAX2
WTK 2.1, includes the libraries you need to develop MIDlets that
take advantage of J2ME WS and also includes a JAX-RPC stub
generator
Good article
http://developers.sun.com/techtopics/mobility/apis/articles/wsa/
32. Timer and TimerTask
• Introduced in J2SE 1.3 to schedule tasks for
execution by a background thread
• MIDP includes the Timer and TimerTask
classes
• Only J2SE classes that are not included in the
CLDC but are included in MIDP
• Timer API identical to J2SE version except
(there’s always an exception…) the constructor
that specifies whether the thread is a daemon
is missing.
33. Timer MIDlet
• Found in com.sb.samples.midlet.timer
package
• Objectives:
1. Introduce Timer and TimerTask
2. Extend RMS basics to include an ‘Updating
Task Records’
34. TimerMIDLet Description
• Creates a TimerTask that does the following
each time it is invoked:
– Scans RecordStore for TaskRows that have 'not
yet been uploaded‘
– Picks the first Task in the list and uploads it via
submitTask()
– Updates it's status value and flags it as 'uploaded‘
– Updates the respective record in the RecordStore
– Makes a sound upon upload
– Rebuilds the UI to reflect a dwindling list of 'not
uploaded' Tasks
35. Developer Note
• We’re not going to walk through the building
of the UI, since we’ve already seen how
Commands are added and handled in other
examples
36. TimerMidlet
public class TimerMidlet extends RMSMidlet {
Command m_CommandRefresh = null;
Timer m_TimerUpload = null;
TimerTask m_TimerTaskUploadATaskRow = null;
public TimerMidlet() {
super();
// need to create a TimerTask that gets the list
of not uploaded TaskRows and
// uploads them (or just the first) and
// updates the record to bUploade = true;
//
m_TimerTaskUploadATaskRow = new TimerTask(){
37. TimerMidlet
public void run() {
try {
// Get the list of un-uploaded Tasks
TaskRow[] aTaskRows = getUnuploadedTaskRows();
if (aTaskRows.length > 0){
TaskRow tr = m_aTaskRows[0];
// Submit them to server
submitTask(tr);
try {
// and update the RMS record
TaskRow.updateRecord(m_RS, tr);
}
42. Use of System Properties
• CLDC does not include the java.util.Properties
class
• Limited set of properties available using
•
•
•
•
System.getProperty( String key)
microedition.platform - Name of the host platform or device
(implementation-dependent)
microedition.encoding - Default character encoding Default
value:“ISO-8859-1”
microedition.configuration - Name and version of the
supported configuration “CLDC-1.1”
microedition.profiles - Names of the supported profiles
(implementation-dependent)
43. Bootcamp Wrap-up
J2ME is an exciting development opportunity
•
• Fairly easy to transition to if you are at Javaeese.
• Need to make some adjustments in your
programming habits (Come to the J2ME Best
Practices session to find out more!)
• J2ME/J2EE integration will be powerful for a
new breed of applications
If your company is interested in the full 2-day
intensive J2ME developer bootcamp or requires
contracting services for mobile projects, contact
me directly at