When you're developing networking MIDlets, pay special attention to blocking operations, such as methods that establish a connection to the network. These can lock up the screen, leaving your user frustrated with your application. To prevent screen lockups, you should perform all blocking operations in a separate thread. As an example, let's assume you've written a method called makeConnection() that establishes a connection to a remote server. You might try to call this method in response to a user click, from the commandAction() event handler:
public class MyNetMIDlet implements CommandListener {
// ...
public void commandAction(Command c, Displayable d) {
if(c == startButton) {
makeConnection(); // BUG: no blocking I/O should be in callback
}
}
public void makeConnection() {
// ...
}
}
|
This approach will lead to screen lockups. To prevent the screen from locking up, have makeConnection() execute in a separate thread. A good way to do so entails having the MIDlet implement the Runnable interface. Instead of invoking makeConnection() directly, commandAction() starts a new thread whose run() method calls makeConnection():
public class MyNetMIDlet implements CommandListener, Runnable {
public void commandAction(Command c, Displayable d) {
if(c == startButton) {
Thread t = new Thread(this);
t.start();
}
}
public void run() {
makeConnection();
}
public void makeConnection() {
// ...
}
}
|
For a more extensive example, consider a networked MIDlet that retrieves the current date and time using the daytime server, which listens on port 13. When a client opens a connection to that port, the server immediately supplies the current date and time. In Listing 1 a MIDlet establishes a socket connection to the time server running on port 13 on a remote machine. The client doesn't send any data; the server treats the new connection itself as a service request, and immediately responds with the current date and time.
Listing 1. TimeMIDlet.java
import javax.microedition.midlet.*;
import javax.microedition.io.*;
import javax.microedition.lcdui.*;
import java.io.*;
public class TimeMIDlet extends MIDlet implements CommandListener {
private static Display display;
private Form f;
private boolean isPaused;
private StringItem si;
private Command exitCommand = new Command("Exit",
Command.EXIT, 1);
private Command startCommand = new Command("GetTime",
Command.ITEM, 1);
public TimeMIDlet() {
display = Display.getDisplay(this);
f = new Form("Time Demo");
si = new StringItem("Select GetTime to get the" +
" current Time!", " ");
f.append(si);
f.addCommand(exitCommand);
f.addCommand(startCommand);
f.setCommandListener(this);
display.setCurrent(f);
}
public void startApp() {
isPaused = false;
}
public void pauseApp() {
isPaused = true;
}
public void destroyApp(boolean unconditional) {
}
public void commandAction(Command c, Displayable s) {
if (c == exitCommand) {
destroyApp(true);
notifyDestroyed();
} else if (c == startCommand) {
getTime();
}
}
public void getTime() {
SocketConnection sc = null;
InputStream is = null;
Form f = new Form("Time Client");
StringItem si = new StringItem("Time:" , "");
f.append(si);
display.setCurrent(f);
try {
sc = (SocketConnection)
Connector.open( "socket://"
+"remoteTimeServer"
+":13");
is = sc.openInputStream();
StringBuffer sb = new StringBuffer();
int c = 0;
while (((c = is.read()) != '\n') && (c != -1)) {
sb.append((char) c);
}
si.setText(sb.toString());
} catch(IOException e) {
Alert a = new Alert("TimeClient",
"Cannot connect to server. "
+ "Ping the server to make sure "
+ "it is running...",
null, AlertType.ERROR);
a.setTimeout(Alert.FOREVER);
display.setCurrent(a);
} finally {
try {
if(is != null) {
is.close();
}
if(sc != null) {
sc.close();
}
} catch(IOException e) {
e.printStackTrace();
}
}
}
}
|
If you run this MIDlet from the J2ME Wireless Toolkit, it displays a
screen like the one in Figure 1, where the emulator prompts you to grant
or deny permission to make the connection. If you allow the connection,
the MIDlet continues normally. If you deny permission, Connector.open()
throws a SecurityException.

Figure 1: Permission Prompt
Unfortunately, you can't actually grant or deny permission, because at
this point the user interface locks up. The interesting thing to note
here is the warning you can see on the J2ME Wireless Toolkit's main
screen, shown in Figure 2, which advises you to avoid deadlocks and
screen lockups by performing blocking operations in a separate thread.

Figure 2: Advice from the Toolkit
To fix this problem, have TimeMIDlet implement the Runnable interface and provide an implementation of the run() method as in Listing 2. Here, run() calls the getTime() method, which performs all the networking in this example. In addition, instead of calling getTime() directly, commandAction() creates a thread and invokes its start() method, which in turn calls the run() method. All changes are highlighted in bold.
Listing 2. TimeMIDlet.java
import javax.microedition.midlet.*;
import javax.microedition.io.*;
import javax.microedition.lcdui.*;
import java.io.*;
public class TimeMIDlet extends MIDlet implements CommandListener, Runnable {
private static Display display;
private Form f;
private boolean isPaused;
private StringItem si;
private Command exitCommand = new Command("Exit", Command.EXIT, 1);
private Command startCommand = new Command("GetTime", Command.ITEM, 1);
public TimeMIDlet() {
display = Display.getDisplay(this);
f = new Form("Time Demo");
si = new StringItem("Select GetTime to get the "
+ "current Time!", " ");
f.append(si);
f.addCommand(exitCommand);
f.addCommand(startCommand);
f.setCommandListener(this);
display.setCurrent(f);
}
public void startApp() {
isPaused = false;
}
public void pauseApp() {
isPaused = true;
}
public void destroyApp(boolean unconditional) {
}
public void commandAction(Command c, Displayable s) {
if (c == exitCommand) {
destroyApp(true);
notifyDestroyed();
} else if (c == startCommand) {
Thread t = new Thread(this);
t.start();
}
}
public void run() {
getTime();
}
public void getTime() {
SocketConnection sc = null;
InputStream is = null;
Form f = new Form("Time Client");
StringItem si = new StringItem("Time:" , "");
f.append(si);
display.setCurrent(f);
try {
sc = (SocketConnection)
Connector.open("socket://"
+ "remoteTimeServer"
+ ":13");
is = sc.openInputStream();
StringBuffer sb = new StringBuffer();
int c = 0;
while (((c = is.read()) != '\n') && (c != -1)) {
sb.append((char) c);
}
si.setText(sb.toString());
} catch(IOException e) {
Alert a = new Alert("TimeClient",
"Cannot connect to server. "
+ "Ping the server to make "
+ "sure it is running...",
null, AlertType.ERROR);
a.setTimeout(Alert.FOREVER);
display.setCurrent(a);
} finally {
try {
if(is != null) {
is.close();
}
if(sc != null) {
sc.close();
}
} catch(IOException e) {
e.printStackTrace();
}
}
}
}
|
For more complex applications, you may want to implement networking and other blocking operations in a separate class that implements the Runnable interface, as explained in "J2ME Low-Level Network Programming in MIDP 2.0."
For more information
- Download the J2ME Wireless Toolkit 2.0
- "Using Threads in J2ME Applications"
- "Networking, User Experience, and Threads"
Acknowledgments
Special thanks to Gary Adams of Sun Microsystems, whose feedback helped improve the article.
About the author
Qusay H. Mahmoud provides Java consulting and training services. He has published dozens of articles on Java, and is the author of Distributed Programming with Java (Manning Publications, 1999) and Learning Wireless Java (O'Reilly, 2002).