The repaint() method and the GUI thread

Examples collected here are intended to illustrate the usefulness of having extra threads in creating a complex GUI.

 
Documentation of repaint() in Component class.

 
Exercise:
Open Java console on your browser and type the letter   t  
Then, your Java console will print all active threads.

 

The GUI (AWT) Thread

One of a number of system threads. This thread is responsible for accepting input events and calling the paint() method. The programmer should leave calling paint() to this thread. Java applets rarely call paint() directly.

There are two ways paint() calls are generated:
  1. spontaneous painting, initiated by the environment
  2. programmer generated calls via repaint() and update()

Spontaneous calls

In the first case, spontaneous painting refers to calls to paint() from the browser.

The GUI thread makes these calls. Every applet or application with a GUI (i.e., a Frame) has a GUI thread.

There are four situations when the GUI thread calls paint() spontaneously.

  1. a window is covered by another and then re-exposed. Paint() is called to reconstruct the damaged parts of the uncovered window
  2. after deiconification
  3. in an applet after init() has finished
  4. when a browser returns to a page which contains an applet, provided the applet is at least partially exposed.

The repaint() Method

The second case, when paint() calls are genereted is when the program calls repaint() or update().

The repaint() method is the one invoked by a program to do drawing. Their are 4 versions of this method but the one with no arguments is usually used. Drawing via repaint() most often takes place in response to user input.

repaint() ==> update() ==(usually calls)==> paint()

repaint() does not invoke paint() directly. It schedules a call to an intermediate method, update(). Finally, update() calls paint() (unless you override update).

The reason for this complexity is Java's support for concurrent programming. It does this using threads.

Using repaint() can be tricky for at least three reasons.

  1. the interaction between repaint() and the spontaneous painting done by the GUI thread
  2. the fact that repaint() just asks the thread system to schedule a call to update()/paint() and then exits. The repaint() method is asynchronous.
  3. the problem of preventing your drawing from being erased when being updated.
All these potential complications are considered in details below.

However, let's start with a discussion what happens if paint() or update() are called directly.
 

Using paint( ) and update() directly.

What is of concern here is what happens if the user, for example, covers all or part of your drawing by another window, and then uncovers your drawing again. Does it survive? Similarly, what happens if the user resizes the browser window?

Not the right way -- RepaintApplet.Java
Source
When paint() is called after init(), or after the applet has been covered or iconified, it uses the default paint() which just paints the background colour, thereby erasing the image. (Note, however, the useful method getGraphics() used in this example.)

Putting drawing in the paint() method, calling paint() directly -- RepaintApplet1.Java
Source
A paint() method has been added. This works but the method paint() is overly complex.

An improvement using update() method to redraw the background --RepaintApplet2.Java
Source
This is a simpler way to do it, because update() automatically covers the drawing area with the background colour, essentially an erasing operation, and then automatically calls paint().

Preventing erasing by update()

Overriding update() to prevent background overlay. -- RepaintApplet2a.Java
Source.


 

Why calling paint() or update() directly is not such a good idea

The paint() method is always called by the AWT main GUI thread. It is possible that other applets are also using that thread. The environment also uses the thread to reconstruct components damaged by, for example, resizing or overlaying or iconifying/deiconifying. If you have a complex paint() method and call it directly you may tie everything else up, thereby degrading performance and defeating the multitasking nature of Java. A bug in your code may not only stop you applet but everything else running on the page as well.

Using update() directly creates the same problems.

The repaint() method is designed to avoid these problems. However it can be tricky to use as it was mentioned before


 

The behaviour of repaint()

The behaviour of the following example is slightly different from the previous examples, if you only consider the applet in isolation. In particular, it implements the different interface: MouseMotionListener interface. But the main difference is that this version is more 'polite' than the others: it uses repaint.

A simple repaint() example -- RepaintApplet3.Java
source
(In this example, drag the red dot with the mouse.)
Another, slightly different version RepaintApplet3a.java
Source
(In this case update() is not overridden.)

If repaint() is just called by user interactions with components you should have no problems using repaint() this way. However, if repaint() is called in a tight loop, the AWT thread queue may be overwhelmed and strange things may happen.

repaint() merely requests the AWT thread to call update(). It then returns immediately. This type of behaviour is called asynchronous. How the AWT thread reacts to the request is up to it. This behavour can lead to problems if you are not careful:

Multiple repaint() requests can unintentionally be combined into one
 

9 Circles Examples

Click the applet and see the expected output
source   (a 1-thread approach).

The behavour which causes the most trouble is the fact that the AWT may combine multiple rapid fire repaint() requests into one request. In practice this behaviour often means that only the last repaint() request in the sequence actually causes update() and paint() to be called. The result is that part of your beautiful drawing goes missing.

This problem usually occurs when repaint() is being called from a program loop.

Example of rapid fire repaint() requests-- CirclesApplet1.java
source (only 1 last circle is painted)

Using Thread.sleep() to slow down repaint() requests

On the other hand, some textbooks recommend that you use the Thread.sleep() method. The idea is to put,

either just before or just after the repaint() call. The Thread.sleep(100) puts the currently running thread to sleep for 100 milliseconds. This allows other threads, if there are any, a chance to run.

When only the AWT thread is present

This doesn't work! Putting the AWT thread to sleep solves nothing. Nothing happens, it wakes up, another repaint() request comes in but the AWT thread goes to sleep again. So the pile up of requests just gets frozen for the sleep time. In the end they are combined the same way as without the sleep and your drawing is still ruined.

Thread.sleep() no help example - CirclesApplet1a.Java
source

In a multithreaded program

Using Thread.sleep() in a programmer created thread to slow down rapid fire repaint() calls solves the problem of combined repaint() requests. The AWT GUI thread keeps running and has time to deal with requests to update() and paint() because the thread which is the source of the requests is put to sleep periodically and thus slowed down. The AWT thread meanwhile goes on its merry way.

Thread.sleep() solves rapid fire repaint() problem -- CirclesApplet2.Java
source

The previous example show that it is necessary to understand more about threads.