Namespaces
Variants
Actions

Using Threads in Java ME

Jump to: navigation, search
SignpostIcon Gear 52.png
Article Metadata

Article
Created: grahamhughes (09 Apr 2011)
Last edited: hamishwillee (01 Oct 2012)

One of the important rules of developing for MIDP is that event methods must return quickly. Since events are serialized, only one event can be processed at a time. While an event is being processed, the application becomes unable to interact with the user. This means that methods like startApp(), commandAction() and keyPressed() (amongst others) cannot perform time-consuming operations like network access. It becomes necessary to use a Thread to perform the operation separately from the event queue.

A part of such use of Threads that people often find difficult is determining when the operation is complete. In this example, we're going to use an interface to supply notification when our time-consuming-thing is complete.

/**
* Interface to be implemented by any object requiring notification about the
* completion of a TimeConsumingThing.
* @author grahamhughes
*/

public interface TimeConsumingThingRequester {
/**
* Notification that the TimeConsumingThing is complete.
* @param done the TimeConsumingThing that has completed
* @param t any throwable thrown from the operation, or null if the
* operation completed successfully
*/

public void timeConsumingThingDone(TimeConsumingThing done, Throwable t);
}

It's pretty simple. You implement this interface, and you'll get a call when the time-consuming-thing finishes.

Time-consuming-things have a pretty common structure, so I've bundled this into an abstract class.

/**
* Abstract superclass for any operation that may take some time (and
* so needs performing in a separate thread).
* @author grahamhughes
*/

public abstract class TimeConsumingThing {
 
private TimeConsumingThingRequester requester;
 
/**
* Subclasses must override this method.
*/

protected abstract void doThing() throws Exception;
 
/**
* Start the operation, notifying the given requester on completion.
* @param r the object requesting notification about completion of the operation
*/

public void go(TimeConsumingThingRequester r) {
requester = r;
(new Thread(new Runner())).start();
}
 
private class Runner implements Runnable {
public void run() {
try {
doThing();
requester.timeConsumingThingDone(TimeConsumingThing.this, null);
} catch (Throwable t) {
requester.timeConsumingThingDone(TimeConsumingThing.this, t);
}
}
}
}

This class handles the threading and the notification, so adding the actual operation is pretty simple. All we need to do as implement doThing() in the subclass.

Here's an example, for retrieving data from an HTTP URL. To keep it simple, it's not a full implementation of getting from an HttpConnection (see the TODOs).

import java.io.DataInputStream;
import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;
 
public class HttpGetter extends TimeConsumingThing {
private String url;
private byte[] data;
 
public HttpGetter(String url) {
this.url = url;
}
 
public byte[] getData() {
return data;
}
 
protected void doThing() throws Exception {
HttpConnection con = (HttpConnection) Connector.open(url);
try {
// TODO check response code
DataInputStream in = con.openDataInputStream();
try {
int size = (int) con.getLength();
if (size >= 0) {
data = new byte[size];
in.readFully(data);
} else {
// TODO handle missing content-length
}
} finally {
in.close();
}
} finally {
con.close();
}
}
}

Here's an example of using the HttpGetter...

import javax.microedition.lcdui.Alert;
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.lcdui.Gauge;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
 
public class Test extends MIDlet implements CommandListener, TimeConsumingThingRequester {
 
private Display display;
 
protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
// empty
}
 
protected void pauseApp() {
// empty
}
 
protected void startApp() throws MIDletStateChangeException {
if (display == null) {
display = Display.getDisplay(this);
Form f = new Form("Example");
f.addCommand(new Command("Start", Command.OK, 0));
f.addCommand(new Command("Exit", Command.EXIT, 0));
f.setCommandListener(this);
display.setCurrent(f);
}
}
 
public void timeConsumingThingDone(TimeConsumingThing done, Throwable t) {
Form f;
 
if (t == null) {
f = new Form("Complete");
f.append("Received " + ((HttpGetter) done).getData().length + " bytes");
} else {
f = new Form("Error");
f.append(t.toString());
}
 
f.addCommand(new Command("Exit", Command.EXIT, 0));
f.setCommandListener(this);
 
display.setCurrent(f);
}
 
public void commandAction(Command c, Displayable d) {
if (c.getCommandType() == Command.EXIT) {
notifyDestroyed();
} else {
// display a "busy" screen
Alert wait = new Alert("Downloading...");
wait.setIndicator(new Gauge(null, false, Gauge.INDEFINITE, Gauge.CONTINUOUS_RUNNING));
display.setCurrent(wait);
// start download
new HttpGetter("http://www.nokia.com").go(this);
}
}
}

Point to note: calling setCurrent() in commandAction() is useful only if commandAction() returns quickly, since the screen won't change until more events can be processed (which can't happen until commandAction() returns, since it blocks the event thread).

This page was last modified on 1 October 2012, at 02:05.
128 page views in the last 30 days.
Nokia Developer aims to help you create apps and publish them so you can connect with users around the world.

京ICP备05048969号  © Copyright Nokia 2013 All rights reserved