Java ME - 06 - Record Stores, Distribution and Localization

3,548 views

Published on

Almost every application needs to store data — e.g. for saving user preferences, highscores or login data. This module explains the concepts behind the record stores, the only way to save data that is included in the MIDP specification and therefore supported on all phones. Data access can be handled conveniently using the DataInputStream and DataOutputStream-classes.This module also covers how to distribute your applications using different project configurations in NetBeans; a process which can involve pre-processing, selective resource inclusion as well as obfuscation. As mobile phones are available on a global market, localization is also a very important aspect. The challenge involves extending the game from the last course so that the game can save the highscore and can be deployed on phones with different screen sizes using specialized graphics.

Contents:

* Record Stores
o Input/OutputStreams
* Distribution
*
o Deployment
o Pre-Processing
o Obfuscation
o OTA-Deployment
* Localization

Published in: Technology, Education
0 Comments
4 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,548
On SlideShare
0
From Embeds
0
Number of Embeds
27
Actions
Shares
0
Downloads
0
Comments
0
Likes
4
Embeds 0
No embeds

No notes for slide

Java ME - 06 - Record Stores, Distribution and Localization

  1. 1. Java™Platform, Micro Edition<br />Part 6 – Record Stores, Distribution andLocalization<br />v3.0a – 14 April 2009<br />1<br />Andreas Jakl, 2009<br />
  2. 2. Disclaimer<br />These slides are provided free of charge at http://www.symbianresources.com and are used during Java ME courses at the University of Applied Sciences in Hagenberg, Austria at the Mobile Computing department ( http://www.fh-ooe.at/mc )<br />Respecting the copyright laws, you are allowed to use them:<br />for your own, personal, non-commercial use<br />in the academic environment<br />In all other cases (e.g. for commercial training), please contact andreas.jakl@fh-hagenberg.at<br />The correctness of the contents of these materials cannot be guaranteed. Andreas Jakl is not liable for incorrect information or damage that may arise from using the materials.<br />This document contains copyright materials which are proprietary to Sun or various mobile device manufacturers, including Nokia, SonyEricsson and Motorola. Sun, Sun Microsystems, the Sun Logo and the Java™ Platform, Micro Edition are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. <br />Andreas Jakl, 2009<br />2<br />
  3. 3. Contents<br />Record Stores<br />Input/OutputStreams<br />Distribution<br />Deployment<br />Pre-Processing<br />Obfuscation<br />OTA-Deployment<br />Localization<br />Andreas Jakl, 2009<br />3<br />
  4. 4. Record Stores<br />Save your data<br />Andreas Jakl, 2009<br />4<br />
  5. 5. Persistent Data<br />Most applications have to store something, e.g.:<br />Settings<br />Current status / progress / high scores<br />User documents and results<br />MIDP requires:<br />Non-volatile memory<br />At least 8kB<br />Full file system access is optional (JSR 75)<br />Andreas Jakl, 2009<br />5<br /><br />
  6. 6. Records: can be thought of rows in a table<br />Record Store & Records<br />Andreas Jakl, 2009<br />6<br />Record Store<br />Integer value, role of a primary key for the database<br />Stores the record data<br />
  7. 7. Record Management System<br />MIDP Record Management System (RMS) works like DBMS<br />Device-independent reading and writing, hides real database-file and location<br />Database = “Record Store”<br />Consists of a number of recordswith automatically assigned, unique IDs: 1, 2, ...<br />Record = Array of bytes<br />To change content: read byte array  modify it  replace original record<br />Andreas Jakl, 2009<br />7<br />
  8. 8. MIDlets and Record Stores<br />MIDlets can access all record stores in their suite<br />With a fully qualified name also from other suites<br />Andreas Jakl, 2009<br />8<br />Record Store “S”<br />Record Store “T”<br />MIDlet Suite A<br />MIDlet 1<br />MIDlet 1<br />Names must be unique within a single MIDlet suite<br />MIDlet 2<br />MIDlet 2<br />Record Store “S”<br />MIDlet Suite B<br />
  9. 9. Record Store Features<br />Record Store saves:<br />Time + Date of last modification<br />Version number (integer), incremented for each operation that modified the contents<br />Individual operations (write, ...) are atomic, synchronous and serialized<br />When MIDlet uses multiple threads to access a single record store, it is responsible for synchronization<br />Andreas Jakl, 2009<br />9<br />
  10. 10. RecordStore API<br />Opening / Creating a Record Store<br />RecordStorers = RecordStore.openRecordStore( String recordStoreName, booleancreateIfNecessary);<br />recordStoreName: MIDlet suite-unique name, up to 32 (Unicode) chars<br />createIfNecessary: if it does not exist, create it or throw an exception<br />Add Records<br />intnewId = rs.addRecord(byte[] data, intoffset, intnumBytes);<br />newId: ID of the record that was created (1, 2, ...)<br />data: byte-array to add as a new record<br />offset: index into data buffer (first byte to write), usually 0<br />numBytes: number of bytes to use (length of data to write)<br />Andreas Jakl, 2009<br />10<br />
  11. 11. RecordStore API<br />Reading a record<br />byte[] record = rs.getRecord(intrecordId);<br />record: copy of the data stored in the record<br />recordId: ID of the record to retrieve<br />Close the record store<br />rs.closeRecordStore();<br />Delete a record store<br />RecordStore.deleteRecordStore(String recordStoreName);<br />Andreas Jakl, 2009<br />11<br />
  12. 12. Byte-Arrays<br />Working with<br />Andreas Jakl, 2009<br />12<br />
  13. 13. Streams<br />Java offers comfortable streams to:<br />Create byte arrays on the fly (ByteArrayOutputStream)<br />Write standard data types to the stream (DataOutputStream)<br />Andreas Jakl, 2009<br />13<br />String object<br />String object<br />DataOutputStream.writeUTF();<br />DataInputStream.readUTF();<br />DataOutputStream<br />DataInputStream<br />ByteArrayOutputStream<br />ByteArrayInputStream<br />byte[]<br />byte[]<br />
  14. 14. Output Stream Example<br />Andreas Jakl, 2009<br />14<br />// Open (+ create) the record store with the name &quot;MyData&quot;<br />RecordStorers = RecordStore.openRecordStore(&quot;MyData&quot;, true);<br />// First create the byte array output stream which can generate the array<br />ByteArrayOutputStreambos = new ByteArrayOutputStream();<br />// On top of that, create the data output stream, <br />// which can serialize several standard Java data types<br />DataOutputStream dos = new DataOutputStream(bos);<br />// Write data to the stream<br />dos.writeInt(667487);<br />dos.writeBoolean(true);<br />dos.writeUTF(&quot;Test&quot;);<br />// Flush data to make sure everything is commited down streams<br />dos.flush();<br />// Grab byte array from the stream<br />byte[] recordOut = bos.toByteArray();<br />// Add a new record to the record store, write the whole array from 0 to its length<br />intnewRecordId = rs.addRecord(recordOut, 0, recordOut.length);<br />// Finished working on the record store – close it<br />dos.close(); // Closes underlying output stream as well<br />rs.closeRecordStore();<br />(example omits catching Exceptions for clarity)<br />
  15. 15. Byte Array<br />Contents of the byte array:<br />Andreas Jakl, 2009<br />15<br />dos.writeInt(667487);<br />dos.writeBoolean(true);<br />dos.writeUTF(&quot;Test&quot;);<br />Int (4 bytes)<br />Boolean (1 byte)<br />Number of bytes used (2 bytes)<br />String<br />UTF-8 encoded String (4 bytes)<br />DataOutputStream-Methods only save data, no type information<br /> you have to remember what you wrote and read it in exactly the same order<br />
  16. 16. Reading<br />Rather similar to writing:<br />Andreas Jakl, 2009<br />16<br />// Open the record store with the name &quot;MyData&quot;<br />RecordStorers = RecordStore.openRecordStore(&quot;MyData&quot;, false);<br />// Get record contents<br />byte[] record = rs.getRecord(newRecordId);<br />// First create the byte array input stream which accesses the byte array<br />ByteArrayInputStreambis = new ByteArrayInputStream(record);<br />// On top of that, create the data input stream, <br />// which can interpret the contents byte array as Java data types<br />DataInputStreamdis = new DataInputStream(bis);<br />// Read data from the stream<br />int version = dis.readInt();<br />booleanfirstStart = dis.readBoolean();<br />String userName = dis.readUTF();<br />// Finished working on the record store – close it<br />dis.close(); // Closes underlying input stream as well<br />rs.closeRecordStore();<br />(example omits catching Exceptions for clarity)<br />
  17. 17. ... back to the RecordStore<br />Enough reading and writing ... <br />Andreas Jakl, 2009<br />17<br />
  18. 18. RecordStore API<br />Modify (= overwrite, replace) existing record<br />rs.setRecord(intrecordId, byte[] newData, intoffset, intnumBytes);<br />recordId: ID of the record to replace<br />data: byte-array to add as a the new record<br />offset: index into data buffer (first byte to write), usually 0<br />numBytes: number of bytes to use (length of data to write)<br />Andreas Jakl, 2009<br />18<br />
  19. 19. Strategy – Settings<br />Saving application settings<br />Write all settings to a byte array into one record of a single record store<br />If possible: Write directly after settings change, not when exiting the application<br />You never know when and how the program is closed(battery removed?)<br />Read on application start-up<br />Use default settings for “file not found”-Exception<br />Andreas Jakl, 2009<br />19<br />
  20. 20. Settings – Existing Record<br />When saving settings, check:<br />New record store was created ->add record<br />Record store already exists -> replace record<br />Andreas Jakl, 2009<br />20<br />if (rs.getNumRecords () == 0) { // Check if record store already contains our settings record<br />rs.addRecord(recordOut, 0, recordOut.length); // Add new record to the store, will get position 1<br />} else {<br />rs.setRecord (1, recordOut, 0, recordOut.length); // Replace previous settings-record<br />}<br />
  21. 21. Settings – Version<br />Save version of data structure to prevent problems after updating application<br />(Version of Record Store only saves # of modifications!)<br />Store version at the beginning of the stream<br />dos.writeShort (1); // Version<br />Check version when reading stream<br />int version = dis.readShort();if (version == 1) { /* Continue parsing */ }<br />Andreas Jakl, 2009<br />21<br />
  22. 22. Strategy – Database<br />Application with database (e.g. time management)<br />Multiple records per record store.Each record = one entry, which consists of the data, written as a byte array as usual<br />Serializing Objects<br />Used to save object’s state to a sequence of bytes<br />Allows rebuilding an identical object later on<br />Not supported in current CLDC<br />Write it yourself: Use a stream to put all relevant data into a stream; read and create obj. in a static method<br />Andreas Jakl, 2009<br />22<br />
  23. 23. Example – Database I<br />Andreas Jakl, 2009<br />23<br />ContactData class, contains information about a person<br />public class ContactData {<br /> private String name; // Saves name of the contact<br /> private int age; // Saves age<br /> public ContactData (String name, int age) {<br /> this.name = name;<br />this.age = age;<br /> }<br />/** Write all relevant data to the stream, which will allow rebuilding the object when reading the stream. */<br /> public void externalize(DataOutputStream dos) throws IOException {<br />dos.writeUTF (name);<br />dos.writeInt (age);<br /> }<br />/** Read data from the stream and use it to create and return a new instance of this object. */<br /> public static ContactDatainternalize(DataInputStreamdis) throws IOException {<br /> String tmpName = dis.readUTF ();<br />inttmpAge = dis.readInt ();<br /> return new ContactData(tmpName, tmpAge);<br /> }<br /> /** Return relevant information in string form. */<br /> public String toString() {<br /> return (&quot;Contact data: Name = &quot; + name + &quot;, Age = &quot; + age);<br /> }<br />}<br />
  24. 24. Example – Database II<br />Andreas Jakl, 2009<br />24<br />Main application – save contacts to the database<br />// Create new vector that will contain all contact data<br />Vector names = new Vector();<br />names.addElement (new ContactData(&quot;Valerie Dimeling&quot;, 20));<br />names.addElement (new ContactData(&quot;Ginger Hay&quot;, 54));<br />try<br />{<br />RecordStore.deleteRecordStore (&quot;myRs&quot;); // Make sure no record store already exists<br />RecordStorers = RecordStore.openRecordStore(&quot;myRs&quot;, true); // Create a new record store<br />ByteArrayOutputStreambos = new ByteArrayOutputStream(); // Create streams for writing data<br />DataOutputStream dos = new DataOutputStream(bos);<br />// Go through the vector and create a record for each element<br /> for (Enumeration e = names.elements (); e.hasMoreElements (); )<br /> {<br />ContactDatacd = (ContactData) e.nextElement (); // Get next element of the vector<br />cd.externalize (dos); // Let the instance externalize itself into our stream<br />dos.flush(); // Make sure everything is written<br /> byte[] record = bos.toByteArray (); // Get byte array from the record<br />rs.addRecord (record, 0, record.length); // Add the record to the store<br />bos.reset (); // Clear the output stream so that we can start from scratch<br /> }<br />dos.close ();<br />[…]<br />
  25. 25. Example – Database III<br />Andreas Jakl, 2009<br />25<br />Main application – restore database from the record store<br />// Read back data using a record enumerator to go through all elements<br /> // Record enumerator would be more powerful, we do not use the advanced features here<br />RecordEnumerationrenum = rs.enumerateRecords (null, null, false);<br /> while (renum.hasNextElement ())<br /> {<br /> byte[] record = renum.nextRecord (); // Get data of the next record<br />ByteArrayInputStreambis = new ByteArrayInputStream(record); // Input streams for parsing data<br />DataInputStreamdis = new DataInputStream(bis);<br />ContactDatacd = ContactData.internalize (dis); // Internalize and create new instance (static method)<br />System.out.println(cd); // Print information to console<br />dis.close();<br /> }<br />rs.closeRecordStore (); // Close the record store<br />} catch (RecordStoreException ex) {<br />ex.printStackTrace();<br />} catch (IOException ex) {<br />ex.printStackTrace();<br />}<br />
  26. 26. Advanced Access<br />Sorting<br />RecordComparator<br />Searching<br />RecordFilter<br />Notification of changes<br />RecordListener<br />Andreas Jakl, 2009<br />26<br />
  27. 27. Distributing MIDlets<br />Let others experience your fabulous applications!<br />Andreas Jakl, 2009<br />27<br />
  28. 28. Device Fragmentation<br />Mobile devices vary in:<br />Screen size<br />Colour depth<br />Speed<br />Optional API support (Bluetooth, ...)<br />Bugs (+ their workarounds)<br />Solution (in NetBeans):<br />Project configurations + pre-processing (like in C++)<br />Andreas Jakl, 2009<br />28<br />
  29. 29. Project Configurations<br />Project Configurations used for<br />Setting variables that are used for pre-processing<br />Including required files to .jar-archive<br />Modify build settings (Obfuscation, ...)<br />Create new configuration<br />Project Properties  Manage Configurations ...  Duplicate one of the configurations<br />Andreas Jakl, 2009<br />29<br />
  30. 30. Adapt Abilities<br />Define variables for pre-processing<br />e.g. ScreenWidth, support for APIs, ...<br />Andreas Jakl, 2009<br />30<br />
  31. 31. Fragment your Code<br />Use pre-processor directives to adapt code depending on abilities of current project configuration<br />Andreas Jakl, 2009<br />31<br />
  32. 32. Adapt Included Resources<br />Choose which files to include for each project configuration:<br />Andreas Jakl, 2009<br />32<br />
  33. 33. Set Active Configuration<br />Right click on the project  “Set Active Project Configuration”: Defines which pre-processor block is active<br />Andreas Jakl, 2009<br />33<br />“SmallScreen”<br />ScreenHeight = 128<br />“MediumScreen”<br />ScreenHeight = 320<br />
  34. 34. Optimization – Obfuscation<br />Original intention:<br />Make reverse engineering more difficult<br />Code more difficult to read after de-compilation<br />Renames classes to “a.class, b.class, …”<br />Removes non-used methods, variables, classes<br /> Significant size reduction<br />Over-the-Air = expensive!<br />MIDlet size restrictions in many phones<br />Improves speed (less code to load / parse)<br />Andreas Jakl, 2009<br />34<br />
  35. 35. Obfuscation<br />Andreas Jakl, 2009<br />35<br />Original archive<br />79.2 kB<br />Obfuscated<br />42.9 kB = 45% smaller!<br />
  36. 36. Define Obfuscation Level<br />For release builds: Activate obfuscation (max. level)<br />Don’t use it for debugging<br />Andreas Jakl, 2009<br />36<br />
  37. 37. Build All Configurations<br />Andreas Jakl, 2009<br />37<br />... creates own directory and .jar/.jad for each configuration<br />Default configuration located in base directory<br />
  38. 38. Over-The-Air Deployment<br />Transmitting MIDlets to phones<br />Andreas Jakl, 2009<br />38<br />
  39. 39. Why OTA for Deployment?<br />Some phones (Samsung, Sagem, BREW,...) do not support installing MIDlets through the PC or Bluetooth<br />Only alternative:<br />Download directly through mobile phone (UTMS)<br /> Over-the-Air (OTA) delivery<br />Andreas Jakl, 2009<br />39<br />
  40. 40. Over-the-Air<br />Andreas Jakl, 2009<br />40<br />HTTP<br />Web Server<br />Mobile Device<br />AMS (Application Management Software)<br />JAD-Server<br />JAR-Server<br />Notification Server<br />GET /midlet.jad<br />GET /midlet.jar<br />POST /install-notify (900 Success)<br />200 OK<br />200 OK<br />200 OK<br />
  41. 41. Web Server – MIME Types<br />Add MIME-Types for .jad and .jar to your web server<br />Required for the phone to correctly handle the files<br />Then, simply provide a link to the .jad-file on your website<br />The .jad-file contains the link to the .jar archive<br />Andreas Jakl, 2009<br />41<br />Example screenshot: SiteAdmin-Tool used at:http://www.site5.com/<br />
  42. 42. Automated Deployment<br />Andreas Jakl, 2009<br />42<br />Make sure the .jar-URL is set correctly in the .jad-file that you upload to your webserver!<br />
  43. 43. Localization<br />Prepare for the international market<br />Andreas Jakl, 2009<br />43<br />
  44. 44. Localization<br />Mobile phones available on a global market<br />Goal: Make source code independent of language, load text for user language from a file<br />Query current language:<br />String locale = System.getProperty (&quot;microedition.locale&quot;);<br />Examples:en-US, en-GB, de-DE<br />NetBeans:<br />Provides ready-made LocalizationSupport-class & tools for managing message bundles<br />Andreas Jakl, 2009<br />44<br /><br />
  45. 45. Add Support Class<br />Andreas Jakl, 2009<br />45<br />
  46. 46. Define Settings<br />You can usually accept the default values:<br />Andreas Jakl, 2009<br />46<br />
  47. 47. Add Localized String<br />Tools  Internationalization  Insert Internationalized String...<br />Andreas Jakl, 2009<br />47<br />Get the localized string from your source code using the static function:<br />Initialization will be done automaticallywhen you retrieve the first message.<br />
  48. 48. Message Bundle<br />Add additional locales for new languages<br />Define same keys in all files<br />Andreas Jakl, 2009<br />48<br />
  49. 49. Thanks for your attention<br />That’s it!<br />Andreas Jakl, 2009<br />49<br />

×