CIT 591 Notes on Debugging Animation Applets
David Matuszek, Fall 2002

It isn't easy to get debugging output from an applet. System.out.print() doesn't always work (depends on how the applet is started), and this means messages about Exceptions don't get displayed. showStatus() is awkward to use from outside the applet class and can only display a single line.

Here's a little class that might help. I haven't documented it, but usage should be obvious.

MessageWindow.java
import java.awt.*;

public class MessageWindow {
    
    Frame frame;
    TextArea text;
    
    // Constructor
    MessageWindow() {
        frame = new Frame();
        text = new TextArea();
        frame.setLayout(new BorderLayout());
        frame.add(text, BorderLayout.CENTER);
        frame.setSize(250, 150);
        frame.setVisible(true);
    }
    
    public void write(String message) {
        text.append(message);
    }
    
    public void writeln(String message) {
        text.append(message + "\n");
    }
    
    public void clear() {
        text.setText("");
    }
}

The most serious problems that people have are getting the display to work properly. If this is your problem, here are some things to check:

Is everything properly initialized in the init() and start() methods?

Here's the basic sequence of events:

  1. The Applet's init() method is called.
  2. The Applet is displayed with all the Java GUI elements in place.
  3. The Applet's start() method is called.
  4. The Applet's paint(Graphics) method is called.

If you get Applet not initialized, that means your program threw an Exception (or Error) before returning from the init() method. You won't see a message about the Exception, because Applets don't provide output, but it won't be a new and mysterious type of error--most likely it's a NullPointerException. You just don't get as much help as usual in finding it.

Once you leave the init() method, Exceptions resulting from uninitialized variables typically cause parts of the Applet to just not do anything. The Applet doesn't exactly "crash," because the browser (or appletviewer) catches the Exception and keeps the Applet running.

Make sure that the model and view have been created, and that the view has been told where to find the model. (I had a lot of trouble finding one of these bugs in my own code.)

Although you define your ActionListeners in the init() method, they are not executed until the appropriate event (such as a button push) occurs. Initialization code in your ActionListeners will not be executed until long after the init() method finishes.

Did you get sizes at the right time?

The size (height and width) of a Component, such as an Applet or a Canvas, is zero until the Component is actually displayed. Your Applet isn't actually displayed until after the init() method finishes, but is done before the Applet's start() method is called. This means you cannot initialize height and width variables in your init() method.

If your model's behavior depends on the display size (for example, a ball bouncing inside a window), you need to get the display size in your Applet's start() method and store it somewhere that your model can find it, before you start the model running.

If you want to allow the display size to be changed, it is a good idea to get the display size right inside your paint(Graphics) method. That way, you always have up-to-date information. You can, of course, get size information in both the start() and the paint(Graphics) methods.

Is your model actually being observed?

Your main model class should extend Observable. Some other class should implement Observer. But this isn't enough--in addition, you must specify myModel.addObserver(myObserver). It doesn't work if your observer doesn't know what to observe.

The observer object should call repaint(), not paint(Graphics). Java will call paint(Graphics) if you call repaint().

When an Applet's paint(Graphics) method is called, it automatically calls the paint(Graphics) method of each of its components. If you have a view class that extends Canvas and has the required public void paint(Graphics) method, then you should be able to make your view class into an observer. I've had somewhat better luck making the Applet class itself the observer, but it can be done either way.

Are you sleeping?

Your display should be repainted not more than about 20 times a second. Use Thread.sleep(50), preferably in your model (so that every change will be displayed). In programs where your model should run at top speed, with the view only being updated occasionally, it can be the viewer that sleeps.

There is no good substitute for sleep.

Are you using deprecated Thread methods?

Don't. Your GUI controls should set variables, which your model should look at from time to time. (For instance: while(okToRun){...}).

Did you start your Thread properly?

The "main" class in your model should either extend Thread or implement Runnable. Either way, it should have a public  void run() method. You do not call this method directly--tell it to start() instead. If you call run() directly, you aren't creating another Thread, you are tying up the current Thread so that it isn't free to do anything else.

Did your Thread die?

Your run() method should contain a loop that continues to run as long as some variable (such as okToRun) is set properly. When run() exits, the Thread dies. After than, your model doesn't run any more.

One way to deal with this is to make certain that run() never exits, but that's generally a bad approach, because you have to put the Thread to sleep, waking occasionally to see whether it's time yet to do anything.

A better approach is simply to let the Thread die when you stop the model. To start the model again, create another Thread and start() it. This really isn't complicated, and it works really well.

How many Threads do you have?

It is possible to give every Object you create its own separate Thread. While Threads aren't terribly expensive, you really don't want to create dozens or hundreds of Threads. If you do, Java will spend so much time switching between them that your program will slow to a crawl.

Does your background flash?

I've said that when your program calls repaint(), Java calls paint(Graphics) for you. That's really an oversimplification. What Java really does is schedule a call to update(Graphics), which is defined approximately as follows:

public void update(Graphics g) {
    g.setColor(Color.white);
    g.fillRect(yourCanvas.getWidth(), yourCanvas.getHeight());
paint(g); }

In other words, what happens is that your canvas is erased before your paint(Graphics) method is called. If you don't like this behavior, you can override update(Graphics)--but if you do, remember to call paint(Graphics) from within your version.

You can also reduce flickering and flashing by painting less--putting fewer rows and columns in your ocean, or making your figures less complicated. There are also ways to completely eliminate flicker from animations, but they are advanced techniques that we haven't talked about. Expect your program to flicker a little bit, and don't worry about it unless it's really irritating.

Are you getting Exceptions you don't even know about?

One symptom of "hidden Exceptions" is that parts of your program just don't do anything. For example, one drawing command works fine and the next one acts like it isn't even there. Maybe an Exception happened in between.

One way of discovering hidden Exceptions is to put your suspicious method body into a try{...} catch(Exception e){...} block. In the catch part, remember that you can't print, so the usual e.printStackTrace() isn't going to be very helpful. However, you can get a less informative but still helpful message by using e.toString(), and displaying it by whatever means is convenient (such as using the MessageWindow class described above).