×
Namespaces

Variants
Actions

Using Threads in Java ME

From Nokia Developer Wiki
Jump to: navigation, search

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).

Version Hint

Windows Phone: [[Category:Windows Phone]]
[[Category:Windows Phone 7.5]]
[[Category:Windows Phone 8]]

Nokia Asha: [[Category:Nokia Asha]]
[[Category:Nokia Asha Platform 1.0]]

Series 40: [[Category:Series 40]]
[[Category:Series 40 1st Edition]] [[Category:Series 40 2nd Edition]]
[[Category:Series 40 3rd Edition (initial release)]] [[Category:Series 40 3rd Edition FP1]] [[Category:Series 40 3rd Edition FP2]]
[[Category:Series 40 5th Edition (initial release)]] [[Category:Series 40 5th Edition FP1]]
[[Category:Series 40 6th Edition (initial release)]] [[Category:Series 40 6th Edition FP1]] [[Category:Series 40 Developer Platform 1.0]] [[Category:Series 40 Developer Platform 1.1]] [[Category:Series 40 Developer Platform 2.0]]

Symbian: [[Category:Symbian]]
[[Category:S60 1st Edition]] [[Category:S60 2nd Edition (initial release)]] [[Category:S60 2nd Edition FP1]] [[Category:S60 2nd Edition FP2]] [[Category:S60 2nd Edition FP3]]
[[Category:S60 3rd Edition (initial release)]] [[Category:S60 3rd Edition FP1]] [[Category:S60 3rd Edition FP2]]
[[Category:S60 5th Edition]]
[[Category:Symbian^3]] [[Category:Symbian Anna]] [[Category:Nokia Belle]]

This page was last modified on 22 July 2013, at 14:23.
82 page views in the last 30 days.
×