Tuesday, November 9, 2010

Check for updates

Cross-posted (in Portuguese) at Technè - Blog de Tecnologia do C.E.S.A.R.

Nowadays it is very common to have a "Check for updates" feature on mobile apps. Specially if they are Android or iPhone apps, because their platform already provide this. And what about Java ME apps? Here is a way.

First we need an official site and a public file in it. For example, http://mysite.com/myapp.txt. This file will have the application latest version number and the url from where it can be downloaded. The following lines will have the description of what have changed:

"[version] [url]

[description]"

Then we add an UI option for this feature, say, "Check for updates". When user selects this option the app downloads the file content with:


InputStream in = Connector.openInputStream("http://mysite.com/myapp.txt");
ByteArrayOutputStream out = new ByteArrayOutputStream();
int i = 0;
while ((i = in.read()) >= 0) {
out.write(i);
}
String content = new String(out.toByteArray());

Current application version may be stored in a constant (final) attribute, but can be read dinamically with MIDlet.getAppProperty(“MIDlet-Version”). To trigger an update the versions need only to be different.

IMPORTANT: description must be used. This clarifies to the user the benefits of updating.

If the user selects to go on with the update the application only need to call
MIDlet.platformRequest(url).
This will open the handset browser to present the url.

As most users do not care about manually checking for new versions we might also add an automatic verifier. If the application is connected this verification can be done in between other connections. If the application is offline it can check how much time has passed since the last check.

Add the following auxiliar methods:


private long byteArrayToLong(byte [] buf) throws IOException {
ByteArrayInputStream in = new ByteArrayInputStream(buf);
DataInputStream dis = new DataInputStream(in);
return dis.readLong();
}
private byte[] longToByteArray(long value) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(out);
dos.writeLong(value);
return out.toByteArray();
}

And the following code on MIDlet.startApp():


RecordStore rs = RecordStore.openRecordStore("lastUpdateCheck", true);
long now = System.currentTimeMillis();
if (rs.getNumRecords() == 0) {
// first application execution
byte[] data = longToByteArray(now);
rs.addRecord(data, 0, data.length);
} else {
final long MONTH = 30*24*60*60*1000;
long before = byteArrayToLong(rs.getRecord(1));
if (now - before > MONTH) {
// show "Check for Update" Alert
// save current time
byte[] data = longToByteArray(now);
rs.setRecord(1, data, 0, data.length);
}
}
rs.closeRecordStore();

When developing an update remember to keep the same RecordStore names and how they are used. Users do not like to loose their configurations. To make sure that this will happen use the same MIDlet-Name and MIDlet-Vendor on jad file.

Combined with Crash Reports, continuous updates add credibility to the application and the development team.
Hope this helps.

Related topics:

No comments: