Multithreading and Animation

Team-Fly

As with any graphic-interface toolkit, threading with the MIDP user-interface classes is a little tricky. The user-interface implementation has its own thread that handles both user interface methods and screen painting. For example, when the user presses a key on their device, the implementation calls keyPressed() in your Canvas subclass. The thread that calls this method belongs to the MIDP implementation. As such, it should be handled with some care. In MIDP implementations, the same thread that calls event methods also calls paint().

Note 

All event-driven user-interface toolkits have this idea of a system-owned user-interface thread. In AWT and Swing, it's called the event dispatch thread. The same rule applies: If you're running inside a thread that doesn't belong to you, don't take all day about it.

Methods that are called by a thread that doesn't belong to you are callbacks. The rule of thumb for callbacks is that you shouldn't do anything that takes a long time. Since the thread doesn't belong to you, you shouldn't hold it up a long time performing your work. Because this thread is responsible for operating the user interface, holding it up with lengthy computations will make your application look lobotomized. Suppose, for example, that you had to retrieve some data from the network. In response to a Command, you might do something like this:

 public void commandAction(Command c, Displayable s) {   if (c == mNetworkCommand) {     // Create a progress screen, progressScreen.     mDisplay.setCurrent(progressForm);     // Now do the network stuff.     // Oops! Users never see progressScreen.   }   // ... } 

The problem is that the progress screen won't be shown. The commandAction() method is called from the user-interface thread, the same thread that's responsible for painting the screen. If you tie up this thread with some lengthy processing of your own, the user-interface thread never has a chance to update the screen. If you need to do something that takes a long time, create a separate thread for it. In the Jargoneer example in Chapter 2, for example, network access was performed in a separate thread.

In certain situations, you will need to ask the user-interface thread to execute code on your behalf. If you are showing an animation, for example, you'll want to make sure that the frames of the animation are properly synchronized with the repainting cycle. Otherwise, you're likely to end up showing frames that are partially drawn.

Display has a mechanism for executing your code in the user-interface thread. It has a method, callSerially(), that accepts a Runnable. When the user-interface thread is ready, meaning when it has finished servicing all repaint requests, it will execute the run() method of the Runnable from the user-interface thread. A typical animation, then, looks like this:

 public class AnimationCanvas     extends Canvas     implements Runnable {   public start() {     run();   }   public void paint(Graphics g) {     // Paint a frame of the animation.   }   public void run() {     // Update our state.     // Now request a paint of the new frame.     repaint();     Display.callSerially(this);   } } 

You'd kick off the animation by calling start(), which in turn would simply call run(). Inside run(), we update our state, call repaint() to request the painting of a new frame. Then we use callSerially() to request that we get called again when the painting is done.

The following example demonstrates this technique. It consists of two classes, Sweep and SweepCanvas. Sweep is a MIDlet that displays the class that actually implements the animation, SweepCanvas. The running SweepCanvas is shown in Figure 9-8.


Figure 9-8: SweepCanvas animation running on a grayscale emulator

This example breaks tradition from the other Canvas examples in this chapter in two regards. First, notice that the animation must be explicitly started. Sweep accomplishes this by calling SweepCanvas's start() method. Furthermore, we need to pass a reference to the MIDlet's Display to SweepCanvas. SweepCanvas uses this reference to make the connection to callSerially() to drive the animation along.

First, here's the source code for Sweep:

 import javax.microedition.lcdui.*; import javax.microedition.midlet.*; public class Sweep     extends MIDlet     {   public void startApp() {     Display display = Display.getDisplay(this);     SweepCanvas sweeper = new SweepCanvas(display);     sweeper.start();     sweeper.addCommand(new Command("Exit", Command.EXIT, 0));     sweeper.setCommandListener(new CommandListener() {       public void commandAction(Command c, Displayable s) {         notifyDestroyed();       }     });     display.setCurrent(sweeper);   }   public void pauseApp() {}   public void destroyApp(boolean unconditional) {} } 

And here's the code for SweepCanvas:

 import javax.microedition.lcdui.*; public class SweepCanvas     extends Canvas     implements Runnable {   private boolean mTrucking;   private int mTheta;   private int mBorder;   Display mDisplay;   public SweepCanvas(Display display) {     mTrucking = true;     mTheta = 0;     mBorder = 10;     mDisplay = display;   }   public void start() {     run();   }   public void stop() {     mTrucking = false;   }   public void paint(Graphics g) {     int width = getWidth();     int height = getHeight();     // Clear the Canvas.     g.setGrayScale(255);     g.fillRect(0, 0, width − 1, height − 1);     g.setGrayScale(0);     int x = mBorder;     int y = mBorder;     int w = width − mBorder * 2;     int h = height − mBorder * 2;     for (int i = 0; i < 8; i++) {       g.setGrayScale((7 − i) * 32);       g.fillArc(x, y, w, h, mTheta + i * 10, 10);       g.fillArc(x, y, w, h, (mTheta + 180) % 360 + i * 10, 10);     }   }   public void run() {     mTheta = (mTheta + 1) % 360;     repaint();     if (mTrucking == true) mDisplay.callSerially(this);   } } 


Team-Fly


Wireless Java. Developing with J2ME
ColdFusion MX Professional Projects
ISBN: 1590590775
EAN: 2147483647
Year: 2000
Pages: 129

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net