More about JSR 75

For today’s JSR adventure, I improved my SMS checkers game — as promised — by adding a feature to read contacts from the handset address book to choose opponents. The new version is available as CheckersPlus. Then I figured that since I’m playing with JSR 75, I should write a sample program to test the other half of JSR 75: the FileConnection API.

I like the elegance of the FileConnection API, and in particular the way it’s designed to function just like the various network connection APIs (using Connector.open with a URL, returning a Connection with an InputStream and and OutputStream just as any a network connection). It’s a very natural way of accessing files both from the Java perspective (where files have traditionally been accessed through the same streams as sockets, etc.) and in the general networking universe (where users are already accustomed to reading local files in an Internet browser using a file://-type url).

Another cool advantage to FileConnection is that you can access parts of the handset’s memory that you can’t touch from MIDP’s standard little RMS memory sandbox. I found by listing the available file roots on my Sagem my700x that I can access the images folder. So for some handsets it may be possible to have a Java MIDlet store a wallpaper or a ringtone as a wallpaper or a ringtone by storing it in the right place where the handset can use it.

But that brings us to the usual disadvantage: you have to know the particular handset very well to know which folders you have access to and how to access them. So it’s great if your project has a small set of target handsets, and not so great if you want your application to be useful on all MIDP handsets across the board. The RMS (Record Management System) has the advantage of always being supported, so it’s probably better to use the RMS if all you want is to set some simple data aside and find it again later.

Of course, even the RMS isn’t as predictible as one might like. There are handsets where RMS access is slower than FileConnection access (and some where it’s not), some that handle individual records in unexpected ways (setting aside a big space for each record even if it’s not used, or failing to free up the memory of a deleted record). Then there’s the fact that the RMS is not convenient for pooling data among multiple MIDlet suites: To share a record store between suites, you need to leave the access door open to every MIDlet on the device, plus the record store is deleted when the associated MIDlet suite is deleted even if other MIDlet suites still wanted to access the data.

So, there’s a bit of a trade-off, meaning it’s better to know both the FileConnection API as well as the RMS, and choose which one to use depending on your application.

For today’s fun, here’s a sample class to illustrate the FileConnection API, doing a few very basic, standard things like listing the available roots and files; reading, writing, creating a test file, etc. As with PIM, FileConnection access is done in its own thread:

package net.frog_parrot.test;

import java.util.Enumeration;
import java.io.*;

import javax.microedition.io.*;
import javax.microedition.io.file.*;

/**
* A simple file Connection testing utility.
*/
public class FCRunner extends Thread {

FCTest myMidlet;

FCRunner(FCTest test) {
myMidlet = test;
}

public void run() {
FileConnection rootdir = null;
try {
Enumeration items = FileSystemRegistry.listRoots();

// Now print the available roots:
while(items.hasMoreElements()) {
String rootname = (String)(items.nextElement());
myMidlet.display(“\n *** new root: ” + rootname);

// open the root directory:
// note there are three slashes before the root name
// because there is no “host” for this connection:
rootdir = (FileConnection)Connector.open(
“file:///” + rootname);
// List the current files:
Enumeration ls = rootdir.list();
while(ls.hasMoreElements()) {
String filename = (String)(ls.nextElement());
myMidlet.display(” file: ” + filename);

// print the contents of the file:
FileConnection file = null;
try {
file = (FileConnection)Connector.open(
“file:///” + rootname + “/” + filename);
if(file.canRead()) {
InputStream is = file.openInputStream();
byte[] contents = new byte[25];
int len = is.read(contents);
is.close();
myMidlet.display(” contents: “
+ new String(contents, 0, len));
} else {
myMidlet.display(” * not readable”);
}
} catch(Exception e) {
e.printStackTrace();
} finally {
try {
file.close();
} catch(Exception e) {}
}
}

// now try to create a file:
FileConnection newfile = null;
try {
newfile = (FileConnection)Connector.open(
“file:///” + rootname + “myNewFile”);
if(newfile.exists()) {
OutputStream os = newfile.openOutputStream();
os.write((new String(“overwriting old contents”)).getBytes());
os.close();
} else {
newfile.create();
OutputStream os = newfile.openOutputStream();
os.write((new String(“creating new contents”)).getBytes());
os.close();
}
} catch(Exception e) {
e.printStackTrace();
} finally {
try {
newfile.close();
} catch(Exception e) {}
}
}
} catch(Exception e) {
e.printStackTrace();
} finally {
try {
rootdir.close();
} catch(Exception e) {}
}
}

}

For completeness, here’s the MIDlet class to use to run the above FileConnection-testing class.  These two classes together make a complete MIDlet that can be easily built by creating a new project for it in the WTK:

package net.frog_parrot.test;

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.midlet.MIDlet;

/**
* A simple MIDlet to test which APIs are implemented.
*/
public class FCTest extends MIDlet implements CommandListener {

private Command myExitCommand = new Command(“Exit”, Command.EXIT, 1);
private Command myOkCommand = new Command(“OK”, Command.OK, 1);
private Form myResultScreen;

/**
* Empty constructor.
*/
public FCTest() {
}

/**
* Initialize the Displayables.
*/
public void startApp() {
myResultScreen = new Form(“Results”);
myResultScreen.addCommand(myExitCommand);
myResultScreen.addCommand(myOkCommand);
myResultScreen.setCommandListener(this);
Display.getDisplay(this).setCurrent(myResultScreen);
}

/**
* Implementation of MIDlet.
*/
public void pauseApp() {
}

/**
* Implementation of MIDlet.
*/
public void destroyApp(boolean unconditional) {
}

/**
* Respond to a button push.
*/
public void commandAction(Command command, Displayable screen) {
if(command == myExitCommand) {
destroyApp(true);
notifyDestroyed();
} else {
FCRunner runner = new FCRunner(this);
runner.start();
}
}

/**
* Append a string to the current display.
*/
public void display(String str) {
myResultScreen.append(str);
myResultScreen.append(“\n”);
}

}

About these ads

No comments yet

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: